MAJOR: Merge develop-v4 into master
This commit is contained in:
commit
ec538c02d8
|
|
@ -1,4 +1,4 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
|
|
|
|||
21
Changes
21
Changes
|
|
@ -2,7 +2,26 @@ Revision history for Verilator
|
|||
|
||||
The contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
* Verilator 3.927 devel
|
||||
* Verilator 4.000 devel
|
||||
|
||||
** This is a major release. Any patches may require major rework to apply.
|
||||
[Thanks everyone]
|
||||
|
||||
** Add multithreaded model generation.
|
||||
|
||||
** Add runtime arguments.
|
||||
|
||||
*** Better optimize large always block splitting, bug1244. [John Coiner]
|
||||
|
||||
*** Add new reloop optimization for repetitive assignment compression.
|
||||
|
||||
**** Fix internals to be C++ null-pointer-check clean.
|
||||
|
||||
**** Fix internals to avoid 'using namespace std'.
|
||||
|
||||
**** Fix Verilation performance issues, bug1316. [John Coiner]
|
||||
|
||||
**** Fix clocker attributes to not propagate on concats. [John Coiner]
|
||||
|
||||
|
||||
* Verilator 3.926 2018-08-22
|
||||
|
|
|
|||
21
Makefile.in
21
Makefile.in
|
|
@ -120,6 +120,7 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
|||
bin/verilator \
|
||||
bin/verilator_coverage \
|
||||
bin/verilator_difftree \
|
||||
bin/verilator_gantt \
|
||||
bin/verilator_includer \
|
||||
bin/verilator_profcfunc \
|
||||
doxygen-mainpage doxygen.config veripool-logo.png \
|
||||
|
|
@ -154,6 +155,7 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
|||
INST_PROJ_FILES = \
|
||||
bin/verilator \
|
||||
bin/verilator_coverage \
|
||||
bin/verilator_gantt \
|
||||
bin/verilator_includer \
|
||||
bin/verilator_profcfunc \
|
||||
include/verilated.mk \
|
||||
|
|
@ -183,8 +185,8 @@ all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
|
|||
.PHONY:verilator_coverage_bin_dbg
|
||||
verilator_exe verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg:
|
||||
@echo ------------------------------------------------------------
|
||||
@echo "making verilator in src" ; \
|
||||
(cd src && $(MAKE) $(OBJCACHE_JOBS) )
|
||||
@echo "making verilator in src"
|
||||
$(MAKE) -C src $(OBJCACHE_JOBS)
|
||||
|
||||
.PHONY:msg_test
|
||||
msg_test: all_nomsg
|
||||
|
|
@ -210,7 +212,7 @@ smoke-test: all_nomsg
|
|||
test_regress/t/t_a_first_sc.pl
|
||||
|
||||
test_regress: all_nomsg
|
||||
@(cd test_regress && $(MAKE))
|
||||
$(MAKE) -C test_regress
|
||||
|
||||
examples: all_nomsg
|
||||
for p in examples/* ; do \
|
||||
|
|
@ -272,12 +274,12 @@ internals.pdf: internals.pod Makefile
|
|||
|
||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||
VL_INST_BIN_FILES = verilator verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg \
|
||||
verilator_coverage verilator_includer verilator_profcfunc
|
||||
verilator_coverage verilator_gantt verilator_includer verilator_profcfunc
|
||||
# Some scripts go into both the search path and pkgdatadir,
|
||||
# so they can be found by the user, and under $VERILATOR_ROOT.
|
||||
|
||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_profcfunc.1
|
||||
VL_INST_MAN_FILES = verilator.1 verilator_coverage.1 verilator_gantt.1 verilator_profcfunc.1
|
||||
|
||||
VL_INST_INC_BLDDIR_FILES = \
|
||||
include/verilated_config.h \
|
||||
|
|
@ -295,10 +297,11 @@ installbin:
|
|||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(bindir)
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator $(DESTDIR)$(bindir)/verilator )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage $(DESTDIR)$(bindir)/verilator_coverage )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_gantt $(DESTDIR)$(bindir)/verilator_gantt )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_profcfunc $(DESTDIR)$(bindir)/verilator_profcfunc )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_bin $(DESTDIR)$(bindir)/verilator_bin )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_bin_dbg $(DESTDIR)$(bindir)/verilator_bin_dbg )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin $(DESTDIR)$(bindir)/verilator_bin )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin_dbg $(DESTDIR)$(bindir)/verilator_bin_dbg )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg )
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/bin
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_includer $(DESTDIR)$(pkgdatadir)/bin/verilator_includer )
|
||||
|
||||
|
|
@ -477,7 +480,7 @@ maintainer-clean::
|
|||
clean mostlyclean distclean maintainer-clean maintainer-copy::
|
||||
for dir in $(SUBDIRS); do \
|
||||
echo making $@ in $$dir ; \
|
||||
(cd $$dir && $(MAKE) $@) ; \
|
||||
$(MAKE) -C $$dir $@ ; \
|
||||
done
|
||||
|
||||
clean mostlyclean distclean maintainer-clean::
|
||||
|
|
|
|||
212
TODO
212
TODO
|
|
@ -5,119 +5,115 @@
|
|||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
|
||||
* Language support:
|
||||
** Fix ordering of each bit separately in a signal (mips)
|
||||
assign b[3:0] = b[7:4]; assign b[7:4] = in;
|
||||
** Support UDP gate primitives/ cell libraries
|
||||
(have code for combos - problem is sequential udps)
|
||||
** Function to eval combo logic after /*verilator public*/ functions [gwaters]
|
||||
** Support generated clocks (correctness)
|
||||
** Recursive functions
|
||||
** Verilog configuration files
|
||||
** Expression coverage (see notes)
|
||||
** Better tristate support
|
||||
** UVM
|
||||
|
||||
Language support:
|
||||
* Fix ordering of each bit separately in a signal (mips)
|
||||
assign b[3:0] = b[7:4]; assign b[7:4] = in;
|
||||
* Support UDP gate primitives/ cell libraries
|
||||
(have code for combos - problem is sequential udps)
|
||||
* Function to eval combo logic after /*verilator public*/ functions [gwaters]
|
||||
* Support generated clocks (correctness)
|
||||
* Real numbers
|
||||
* Recursive functions
|
||||
* Verilog configuration files
|
||||
* Structs/unions (have starting point)
|
||||
* DPI to define C/C++ calls from Verilog
|
||||
* Expression coverage (see notes)
|
||||
* Better tristate support
|
||||
* Long-term Features
|
||||
** Assertions
|
||||
** Tristate support
|
||||
|
||||
Long-term Features
|
||||
* Assertions
|
||||
* Tristate support
|
||||
* Multithreaded execution
|
||||
* Configure/Make/Install
|
||||
** Distribute with flex/bison already expanded?
|
||||
Flex library not needed. Probably too difficult to be worth it.
|
||||
|
||||
Configure/Make/Install
|
||||
* Distribute with flex/bison already expanded?
|
||||
Flex library not needed. Probably too difficult to be worth it.
|
||||
* Testing:
|
||||
** Capture all inputs into global "rerun it" file
|
||||
** Code to make wrapper that sets signals, so can do comparison checks
|
||||
** New random program generator
|
||||
** Better graph viewer with search and zoom
|
||||
** Port and test against opencores.org code
|
||||
** // verilator debug in code so can see only tree affecting those nodes
|
||||
|
||||
Testing:
|
||||
* Capture all inputs into global "rerun it" file
|
||||
* Code to make wrapper that sets signals, so can do comparison checks
|
||||
* New random program generator
|
||||
* Better graph viewer with search and zoom
|
||||
* Port and test against opencores.org code
|
||||
* // verilator debug in code so can see only tree affecting those nodes
|
||||
* Usability:
|
||||
** Detect and pre-remove most UNOPTFLATs
|
||||
** Better reporting of unopt problems, including what lines of code
|
||||
** Report more errors (all of them?) before exiting [Eugene Weber]
|
||||
** Auto-create scons config files
|
||||
** Print version/etc message at runtime. (4.000?)
|
||||
Include number of lines of code, percent comments, code complexity measurement
|
||||
<-80chars------------------------------------------------------------------->
|
||||
Verilator 3.600 - The fast free open-sourced simulator. Copyright 2001-2013.
|
||||
Verilated #### modules, #### instances, ##### sigs,
|
||||
#### non-comment lines, ##### ops, ### KB model size
|
||||
|
||||
Usability:
|
||||
* Detect and pre-remove most UNOPTFLATs (4.000)
|
||||
* Better reporting of unopt problems, including what lines of code
|
||||
* Report more errors (all of them?) before exiting [Eugene Weber]
|
||||
* Auto-create scons config files
|
||||
* Print version/etc message at runtime. (4.000?)
|
||||
Include number of lines of code, percent comments, code complexity measurement
|
||||
<-80chars------------------------------------------------------------------->
|
||||
Verilator 3.600 - The fast free open-sourced simulator. Copyright 2001-2013.
|
||||
Verilated #### modules, #### instances, ##### sigs,
|
||||
#### non-comment lines, ##### ops, ### KB model size
|
||||
* Lint:
|
||||
** CDCRSTLOGIC should allow filtering with paths
|
||||
"waive CDCRSTLOGIC --from a.b.sig --to a.c.sig --via OR"
|
||||
|
||||
Lint:
|
||||
* CDCRSTLOGIC should allow filtering with paths
|
||||
"waive CDCRSTLOGIC --from a.b.sig --to a.c.sig --via OR"
|
||||
* Internal Code:
|
||||
** A Visitor class that understands how to traverse data types
|
||||
** V3Graph should be templated container type, taking in Vertex + Edge types
|
||||
** Instead of string, have an VEncodedString/VIdString which contains __DOT__ish
|
||||
things, to reduce bugs. Also add _20 trailing space to \ encoded names.
|
||||
|
||||
Internal Code:
|
||||
* A Visitor class that understands how to traverse data types
|
||||
* V3Graph should be templated container type, taking in Vertex + Edge types
|
||||
* Instead of string, have an VEncodedString/VIdString which contains __DOT__ish
|
||||
things, to reduce bugs. Also add _20 trailing space to \ encoded names. (4.000)
|
||||
* Runtime:
|
||||
** New evalulation loop ~/src/verilator/notes/event_loop.txt (4.000?)
|
||||
** Remove all private internal functions from top level wrapper header, move
|
||||
to new level
|
||||
** Completely standalone simulation
|
||||
main() records arguments for $test$plusvars
|
||||
instantiates top,
|
||||
does tracing (support $dump?)
|
||||
calls top->simulateForever()
|
||||
exits
|
||||
|
||||
Runtime:
|
||||
* New evalulation loop ~/src/verilator/notes/event_loop.txt (4.000?)
|
||||
* Remove all private internal functions from top level wrapper header, move
|
||||
to new level (4.000?)
|
||||
* Completely standalone simulation (4.000)
|
||||
main() records arguments for $test$plusvars
|
||||
instantiates top,
|
||||
does tracing (support $dump?)
|
||||
calls top->simulateForever()
|
||||
exits
|
||||
|
||||
Performance:
|
||||
* Latch optimizations
|
||||
* Constant propagation
|
||||
Extra cleaning AND: 1 & ((VARREF >> 1) | ((&VARREF >> 1) & VARREF))
|
||||
Extra shift (perhaps due to clean): if (1 & CAST (VARREF >> #))
|
||||
* Gated clock and latch conversion to flops. [JeanPaul Vanitegem]
|
||||
Could propagate the AND into pos/negedges and let domaining optimize.
|
||||
* Negedge reset
|
||||
Switch to remove negedges that don't matter
|
||||
Can't remove async resets from control flops (like in syncronizers)
|
||||
* If all references to array have a constant index, blow up into separate signals-per-index
|
||||
* Bit-multiply for faster bit swapping and a=b[1,3,2] random bit reorderings.
|
||||
* Move _last sets and all other combo logic inside master
|
||||
if() that triggers on all possible sense items
|
||||
* Rewrite and combine V3Life, V3Subst
|
||||
If block temp only ever set in one place to constant, propagate it
|
||||
Used in t_mem for array delayed assignments
|
||||
Replace variables if set later in same cfunc branch
|
||||
See for example duplicate sets of _narrow in cycle 90/91 of t_select_plusloop
|
||||
* Same assignment on both if branches
|
||||
"if (a) { ... b=2; } else { ... b=2;}" -> "b=2; if ..."
|
||||
Careful though, as b could appear in the statement or multiple times in statement
|
||||
(Could just require exatly two 'b's in statement)
|
||||
* Simplify XOR/XNOR/AND/OR bit selection trees
|
||||
Foo = A[1] ^ A[2] ^ A[3] etc are better as ^ ( A & 32'b...1110 )
|
||||
* Combine variables into wider elements
|
||||
Parallel statements on different bits should become single signal
|
||||
Variables that are always consumed in "parallel" can be joined
|
||||
* Duplicate assignments in gate optimization
|
||||
Common to have many separate posedge blocks, each with identical
|
||||
reset_r <= rst_in
|
||||
* If signal is used only once (not counting trace), always gate substitute
|
||||
Don't merge if any combining would form circ logic (out goes back to in)
|
||||
* Multiple assignments each bit can become single assign with concat
|
||||
Make sure a SEL of a CONCAT can get the single bit back.
|
||||
* Usually blocks/values
|
||||
Enable only after certain time, so VL_TIME_I(32) > 0x1e gets eliminated out
|
||||
* Better ordering of a<=b, b<=c, put all refs to 'b' next to each other to optimize caching
|
||||
* I-cache packing improvements (what/how?)
|
||||
* Data cache organization (order of vars in class)
|
||||
First have clocks,
|
||||
then bools instead of uint32_t's
|
||||
then based on what sense list they come from, all outputs, then all inputs
|
||||
finally have any signals part of a "usually" block, or constant.
|
||||
* Rather then tracking widths, have a MSB...LSB of this expression
|
||||
(or better, a bitmask of bits relevant in this expression)
|
||||
* Track recirculation and convert into clock-enables
|
||||
* Clock enables should become new clocking domains for speed
|
||||
* If floped(a) & flopped(b) and no other a&b, then instead flop(a&b).
|
||||
* Sort by output bitselects so can combine more assignments (see DDP example dx_dm signal)
|
||||
* Performance:
|
||||
** Latch optimizations
|
||||
** Constant propagation
|
||||
Extra cleaning AND: 1 & ((VARREF >> 1) | ((&VARREF >> 1) & VARREF))
|
||||
Extra shift (perhaps due to clean): if (1 & CAST (VARREF >> #))
|
||||
** Gated clock and latch conversion to flops. [JeanPaul Vanitegem]
|
||||
Could propagate the AND into pos/negedges and let domaining optimize.
|
||||
** Negedge reset
|
||||
Switch to remove negedges that don't matter
|
||||
Can't remove async resets from control flops (like in syncronizers)
|
||||
** If all references to array have a constant index, blow up into separate signals-per-index
|
||||
** Bit-multiply for faster bit swapping and a=b[1,3,2] random bit reorderings.
|
||||
** Move _last sets and all other combo logic inside master
|
||||
if() that triggers on all possible sense items
|
||||
** Rewrite and combine V3Life, V3Subst
|
||||
If block temp only ever set in one place to constant, propagate it
|
||||
Used in t_mem for array delayed assignments
|
||||
Replace variables if set later in same cfunc branch
|
||||
See for example duplicate sets of _narrow in cycle 90/91 of t_select_plusloop
|
||||
** Same assignment on both if branches
|
||||
"if (a) { ... b=2; } else { ... b=2;}" -> "b=2; if ..."
|
||||
Careful though, as b could appear in the statement or multiple times in statement
|
||||
(Could just require exatly two 'b's in statement)
|
||||
** Simplify XOR/XNOR/AND/OR bit selection trees
|
||||
Foo = A[1] ^ A[2] ^ A[3] etc are better as ^ ( A & 32'b...1110 )
|
||||
** Combine variables into wider elements
|
||||
Parallel statements on different bits should become single signal
|
||||
Variables that are always consumed in "parallel" can be joined
|
||||
** Duplicate assignments in gate optimization
|
||||
Common to have many separate posedge blocks, each with identical
|
||||
reset_r <= rst_in
|
||||
** If signal is used only once (not counting trace), always gate substitute
|
||||
Don't merge if any combining would form circ logic (out goes back to in)
|
||||
** Multiple assignments each bit can become single assign with concat
|
||||
Make sure a SEL of a CONCAT can get the single bit back.
|
||||
** Usually blocks/values
|
||||
Enable only after certain time, so VL_TIME_I(32) > 0x1e gets eliminated out
|
||||
** Better ordering of a<=b, b<=c, put all refs to 'b' next to each other to optimize caching
|
||||
** I-cache packing improvements (what/how?)
|
||||
** Data cache organization (order of vars in class)
|
||||
First have clocks,
|
||||
then bools instead of uint32_t's
|
||||
then based on what sense list they come from, all outputs, then all inputs
|
||||
finally have any signals part of a "usually" block, or constant.
|
||||
** Rather then tracking widths, have a MSB...LSB of this expression
|
||||
(or better, a bitmask of bits relevant in this expression)
|
||||
** Track recirculation and convert into clock-enables
|
||||
** Clock enables should become new clocking domains for speed
|
||||
** If floped(a) & flopped(b) and no other a&b, then instead flop(a&b).
|
||||
** Sort by output bitselects so can combine more assignments (see DDP example dx_dm signal)
|
||||
|
|
|
|||
238
bin/verilator
238
bin/verilator
|
|
@ -338,6 +338,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||
--pipe-filter <command> Filter all input through a script
|
||||
--prefix <topname> Name of top level class
|
||||
--prof-cfuncs Name functions for profiling
|
||||
--prof-threads Enable generating gantt chart data for threads
|
||||
--private Debugging; see docs
|
||||
--public Debugging; see docs
|
||||
-pvalue+<name>=<value> Overwrite toplevel parameter
|
||||
|
|
@ -350,6 +351,9 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||
--stats-vars Provide statistics on variables
|
||||
-sv Enable SystemVerilog parsing
|
||||
+systemverilogext+<ext> Synonym for +1800-2017ext+<ext>
|
||||
--threads <threads> Enable multithreading
|
||||
--threads-dpi <mode> Enable multithreaded DPI
|
||||
--threads-max-mtasks <mtasks> Tune maximum mtask partitioning
|
||||
--top-module <topname> Name of top level input module
|
||||
--trace Enable waveform creation
|
||||
--trace-depth <levels> Depth of tracing
|
||||
|
|
@ -380,6 +384,19 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||
--x-initial-edge Enable initial X->0 and X->1 edge triggers
|
||||
-y <dir> Directory to search for modules
|
||||
|
||||
This is a short summary of the arguments to run-time Verilated arguments.
|
||||
detailed descriptions in L</"RUNTIME ARGUMENTS"> for more information.
|
||||
|
||||
+verilator+debug Enable debugging
|
||||
+verilator+debugi+<value> Enable debugging at a level
|
||||
+verilator+help Display help
|
||||
+verilator+prof+threads+file+I<filename> Set profile filename
|
||||
+verilator+prof+threads+start+I<value> Set profile starting point
|
||||
+verilator+prof+threads+window+I<value> Set profile duration
|
||||
+verilator+rand+reset+<value> Set random reset technique
|
||||
+verilator+V Verbose version and config
|
||||
+verilator+version Show version and exit
|
||||
|
||||
|
||||
=head1 VERILATION ARGUMENTS
|
||||
|
||||
|
|
@ -1070,6 +1087,18 @@ Verilog module and line number the statement came from. This allows gprof
|
|||
or oprofile reports to be correlated with the original Verilog source
|
||||
statements. See also L<verilator_profcfunc>.
|
||||
|
||||
=item --prof-threads
|
||||
|
||||
Enable gantt chart data collection for threaded builds.
|
||||
|
||||
Verilator will record the start and end time of each macro-task across a
|
||||
number of calls to eval. (What is a macro-task? See the Verilator internals
|
||||
document.)
|
||||
|
||||
When profiling is enabled, the runtime will emit a blurb of profiling data
|
||||
in non-human-friendly form. The C<verilator_gantt> script will transform
|
||||
this into a nicer visual format and produce some related statistics.
|
||||
|
||||
=item --private
|
||||
|
||||
Opposite of --public. Is the default; this option exists for backwards
|
||||
|
|
@ -1124,7 +1153,10 @@ Enable including save and restore functions in the generated model.
|
|||
|
||||
The user code must create a VerilatedSerialize or VerilatedDeserialze
|
||||
object then calling the << or >> operators on the generated model and any
|
||||
other data the process needs saved/restored. For example:
|
||||
other data the process needs saved/restored. These functions are not
|
||||
thread safe, and are typically called only by a main thread.
|
||||
|
||||
For example:
|
||||
|
||||
void save_model(const char* filenamep) {
|
||||
VerilatedSave os;
|
||||
|
|
@ -1163,6 +1195,42 @@ compatibility with other simulators.
|
|||
|
||||
A synonym for C<+1800-2017ext+>I<ext>.
|
||||
|
||||
=item --threads I<threads>
|
||||
|
||||
=item --no-threads
|
||||
|
||||
With --threads 0 or --no-threads, the default, the generated model is not
|
||||
thread safe. With --threads 1, the generated model is single threaded but
|
||||
may run in a multithreaded environment. With --threads N, where N >= 2, the
|
||||
model is generated to run multithreaded on up to N threads. See
|
||||
L</"MULTITHREADING">.
|
||||
|
||||
=item --threads-dpi all
|
||||
|
||||
=item --threads-dpi none
|
||||
|
||||
=item --threads-dpi pure
|
||||
|
||||
When using --dpi with --threads, control what DPI tasks are thread safe.
|
||||
|
||||
With --threads-dpi all, enable Verilator to assume all DPI imports are
|
||||
threadsafe, and to use thread-local storage for communication with DPI,
|
||||
potentially improving performance. Any DPI libraries need appropriate
|
||||
mutexes to avoid undefined behavior.
|
||||
|
||||
With --threads-dpi none, Verilator assume DPI imports are not thread safe,
|
||||
and Verilator will serialize calls to DPI imports by default, potentially
|
||||
harming performance.
|
||||
|
||||
With --threads-dpi pure, the default, Verilator assumes DPI pure imports
|
||||
are threadsafe, but non-pure DPI imports are not.
|
||||
|
||||
=item --threads-max-mtasks I<value>
|
||||
|
||||
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 --top-module I<topname>
|
||||
|
||||
When the input Verilog contains more than one top level module, specifies
|
||||
|
|
@ -1432,6 +1500,67 @@ are desired for error messages instead of relative filenames.
|
|||
=back
|
||||
|
||||
|
||||
=head1 RUNTIME ARGUMENTS
|
||||
|
||||
The following are the arguments that may be passed to a Verilated
|
||||
executable, provided that executable calls Verilated::commandArgs().
|
||||
|
||||
All runtime arguments begin with +verilator, so that the user's executable
|
||||
may skip over all +verilator arguments when parsing its command line.
|
||||
|
||||
=over 4
|
||||
|
||||
=item +verilator+debug
|
||||
|
||||
Enable debugging. Equivalent to +verilator+debugi+4.
|
||||
|
||||
=item +verilator+debugi+I<value>
|
||||
|
||||
Enable debugging at the provided level.
|
||||
|
||||
=item +verilator+help
|
||||
|
||||
Display help and exit.
|
||||
|
||||
=item +verilator+prof+threads+file+I<filename>
|
||||
|
||||
When using --prof-threads, the filename to dump to. Defaults to
|
||||
"profile_threads.dat".
|
||||
|
||||
=item +verilator+prof+threads+start+I<value>
|
||||
|
||||
When using --prof-threads, 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.
|
||||
|
||||
=item +verilator+prof+threads+window+I<value>
|
||||
|
||||
When using --prof-threads, after $time reaches
|
||||
+verilator+prof+threads+start, Verilator will warm up the profiling for
|
||||
this number of eval() calls, then will capture the profiling of this number
|
||||
of eval() calls. Defaults to 2, which makes sense for a
|
||||
single-clock-domain module where it's typical to want to capture one
|
||||
posedge eval() and one negedge eval().
|
||||
|
||||
=item +verilator+rand+reset+I<value>
|
||||
|
||||
When a model was Verilated using "-x-inital unique", sets the
|
||||
initialization technique. 0 = Reset to zeros. 1 = Reset to all-ones. 2 =
|
||||
Randomize. See L</"Unknown states">.
|
||||
|
||||
=item +verilator+V
|
||||
|
||||
Shows the verbose version, including configuration information.
|
||||
|
||||
=item +verilator+version
|
||||
|
||||
Displays program version and exits.
|
||||
|
||||
=back
|
||||
|
||||
|
||||
=head1 EXAMPLE C++ EXECUTION
|
||||
|
||||
We'll compile this example into C++.
|
||||
|
|
@ -1586,6 +1715,9 @@ compile times, and --x-assign=fast --x-initial=fast may increase the risk
|
|||
of reset bugs in trade for performance; see the above documentation for
|
||||
these flags.
|
||||
|
||||
If using Verilated multithreaded, use C<numactl> to ensure you are using
|
||||
non-conflicting hardware resources. See L</"MULTITHREADING">.
|
||||
|
||||
Minor Verilog code changes can also give big wins. You should not have any
|
||||
UNOPTFLAT warnings from Verilator. Fixing these warnings can result in
|
||||
huge improvements; one user fixed their one UNOPTFLAT warning by making a
|
||||
|
|
@ -2127,6 +2259,91 @@ the names of the .cpp files to compile in from the make variables generated
|
|||
in obj_dir/Vour_classes.mk.
|
||||
|
||||
|
||||
=head1 MULTITHREADING
|
||||
|
||||
Verilator experimentally supports multithreading.
|
||||
|
||||
With --no-threads, the default, the model is not thread safe, and any use
|
||||
of more than one thread calling into one or even different Verilated models
|
||||
may result in unpredictable behavior. This gives the highest single thread
|
||||
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.
|
||||
|
||||
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()
|
||||
provides one of those threads, and the generated model will create and
|
||||
manage the other N-1 threads. It's the client's responsibility not to
|
||||
oversubscribe the available CPU cores. Under CPU oversubscription, the
|
||||
Verilated model should not livelock nor deadlock, however, you can expect
|
||||
performance to be far worse than it would be with proper stoichiometry of
|
||||
threads and CPU cores.
|
||||
|
||||
The remainder of this section describe behavior with --threads 1 or
|
||||
--threads N (not --no-threads).
|
||||
|
||||
VL_THREADED is defined when compiling a threaded Verilated module, causing
|
||||
the Verilated support classes become threadsafe.
|
||||
|
||||
The thread used for constructing a model must the the same thread that
|
||||
calls eval() into the model, this is called the "eval thread". The thread
|
||||
used to perform certain global operations such as saving and tracing must
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
egrep 'processor|physical id|core id' /proc/cpuinfo
|
||||
|
||||
To select cores 0, 1, 2, and 3 that are all located on the same socket (0)
|
||||
but different physical cores. (Also useful is "numactl --hardware", or
|
||||
C<lscpu> but those doesn't show Hyperthreading cores.) Then we execute
|
||||
|
||||
numactl -m 0 -C 0,1,2,3 -- verilated_executable_name
|
||||
|
||||
This will limit memory to socket 0, and threads to cores 0, 1, 2, 3,
|
||||
(presumably on socket 0) optimizing performance. Of course this must be
|
||||
adjusted if you want another simulator using e.g. socket 1, or if you
|
||||
Verilated with a different number of threads. To see what CPUs are
|
||||
actually used, use --prof-threads.
|
||||
|
||||
=head2 Multithreaded Verilog and Library Support
|
||||
|
||||
$display/$stop/$finish are delayed until the end of an eval() call in order
|
||||
to maintain ordering between threads. This may result in additional tasks
|
||||
completing after the $stop or $finish.
|
||||
|
||||
=over 4
|
||||
|
||||
If using --coverage, the coverage routines are fully thread safe.
|
||||
|
||||
If using --dpi, Verilator assumes pure DPI imports are thread safe,
|
||||
balancing performance versus saftey. See --threads-dpi.
|
||||
|
||||
If using --savable, the save/restore classes are not multithreaded and are
|
||||
must be called only by the eval thread.
|
||||
|
||||
If using --sc, the SystemC kernel is not thread safe, therefore the eval
|
||||
thread and main thread must be the same.
|
||||
|
||||
If using --trace, the tracing classes must be constructed and called from
|
||||
the main thread.
|
||||
|
||||
If using --vpi, since SystemVerilog VPI was not architected by IEEE to be
|
||||
multithreaded, Verilator requires all VPI calls are only made from the main
|
||||
thread.
|
||||
|
||||
=back
|
||||
|
||||
=head1 CONFIGURATION FILES
|
||||
|
||||
In addition to the command line, warnings and other features may be
|
||||
|
|
@ -3591,6 +3808,21 @@ section for more details.
|
|||
Ignoring this warning will only slow simulations, it will simulate
|
||||
correctly.
|
||||
|
||||
=item UNOPTTHREADS
|
||||
|
||||
Warns that the thread scheduler was unable to partition the design to fill
|
||||
the requested number of threads.
|
||||
|
||||
One workaround is to request fewer threads with C<--threads>.
|
||||
|
||||
Another possible workaround is to allow more MTasks in the runtime, by
|
||||
increasing the value of --threads-max-mtasks. More MTasks will result in
|
||||
more communication and synchronization overhead at runtime; the scheduler
|
||||
attempts to minimize the number of MTasks for this reason.
|
||||
|
||||
Ignoring this warning will only slow simulations, it will simulate
|
||||
correctly.
|
||||
|
||||
=item UNPACKED
|
||||
|
||||
Warns that unpacked structs and unions are not supported.
|
||||
|
|
@ -4140,6 +4372,8 @@ performance gain.
|
|||
|
||||
In 2009, major SystemVerilog and DPI language support was added.
|
||||
|
||||
In 2018, Verilator 4.000 was released with multithreaded support.
|
||||
|
||||
Currently, various language features and performance enhancements are added
|
||||
as the need arises. Verilator is now about 3x faster than in 2002, and is
|
||||
faster than many popular commercial simulators.
|
||||
|
|
@ -4240,7 +4474,7 @@ License Version 2.0.
|
|||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<verilator_coverage>, L<verilator_profcfunc>, L<make>,
|
||||
L<verilator_coverage>, L<verilator_gantt>, L<verilator_profcfunc>, L<make>,
|
||||
|
||||
L<verilator --help> which is the source for this document,
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,560 @@
|
|||
: # -*-Mode: perl;-*- use perl, wherever it is
|
||||
eval 'exec perl -wS $0 ${1+"$@"}'
|
||||
if 0;
|
||||
# See copyright, etc in below POD section.
|
||||
######################################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Getopt::Long;
|
||||
use IO::File;
|
||||
use Pod::Usage;
|
||||
use vars qw ($Debug);
|
||||
|
||||
$Debug = 0;
|
||||
my $Opt_File;
|
||||
my $Opt_Time_Per_Char = 0; # rdtsc ticks per char in gantt chart, 0=auto
|
||||
my $opt_vcd = "profile_threads.vcd";
|
||||
|
||||
our %Threads;
|
||||
our %Mtasks;
|
||||
our %Global;
|
||||
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
Getopt::Long::config("no_auto_abbrev");
|
||||
if (! GetOptions(
|
||||
"help" => \&usage,
|
||||
"scale=i" => \$Opt_Time_Per_Char,
|
||||
"debug" => sub { $Debug = 1; },
|
||||
"vcd=s" => \$opt_vcd,
|
||||
"no-vcd!" => sub { $opt_vcd = undef; },
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
die "%Error: Bad usage, try 'verilator_gantt --help'\n";
|
||||
}
|
||||
|
||||
$Opt_File = "profile_threads.dat" if !defined $Opt_File;
|
||||
|
||||
process($Opt_File);
|
||||
write_vcd($opt_vcd) if defined $opt_vcd;
|
||||
exit(0);
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub usage {
|
||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
sub parameter {
|
||||
my $param = shift;
|
||||
if (!defined $Opt_File) {
|
||||
$Opt_File = $param;
|
||||
} else {
|
||||
die "%Error: Unknown parameter: $param\n";
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub process {
|
||||
my $filename = shift;
|
||||
|
||||
read_data($filename);
|
||||
report();
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub read_data {
|
||||
my $filename = shift;
|
||||
|
||||
%Global = (rdtsc_cycle_time => 0);
|
||||
|
||||
my $fh = IO::File->new ($filename) or die "%Error: $! $filename,";
|
||||
while (my $line = $fh->getline) {
|
||||
if ($line =~ m/VLPROF mtask\s(\d+)\sstart\s(\d+)\send\s(\d+)\selapsed\s(\d+)\spredict_time\s(\d+)\scpu\s(\d+)\son thread (\d+)/) {
|
||||
my $mtask = $1;
|
||||
my $start = $2;
|
||||
my $end = $3;
|
||||
my $elapsed_time = $4;
|
||||
my $predict_time = $5;
|
||||
my $cpu = $6;
|
||||
my $thread = $7;
|
||||
$Threads{$thread}{$start}{mtask} = $mtask;
|
||||
$Threads{$thread}{$start}{end} = $end;
|
||||
$Threads{$thread}{$start}{cpu} = $cpu;
|
||||
|
||||
if (!exists $Mtasks{$mtask}{elapsed}) {
|
||||
$Mtasks{$mtask}{elapsed} = 0;
|
||||
}
|
||||
$Mtasks{$mtask}{elapsed} += $elapsed_time;
|
||||
$Mtasks{$mtask}{predict} = $predict_time;
|
||||
$Mtasks{$mtask}{end} = max($Mtasks{$mtask}{end}, $end);
|
||||
}
|
||||
elsif ($line =~ /^VLPROFTHREAD/) {}
|
||||
elsif ($line =~ m/VLPROF arg\s+(\S+)\+([0-9.])\s*$/
|
||||
|| $line =~ m/VLPROF arg\s+(\S+)\s+([0-9.])\s*$/) {
|
||||
$Global{args}{$1} = $2;
|
||||
}
|
||||
elsif ($line =~ m/VLPROF stat\s+(\S+)\s+([0-9.]+)/) {
|
||||
$Global{stats}{$1} = $2;
|
||||
}
|
||||
elsif ($line =~ /^#/) {}
|
||||
elsif ($Debug) {
|
||||
chomp $line;
|
||||
print "Unk: $line\n";
|
||||
}
|
||||
# TODO -- this is parsing text printed by a client.
|
||||
# Really, verilator proper should generate this
|
||||
# if it's useful...
|
||||
if ($line =~ m/rdtsc time = (\d+) ticks/) {
|
||||
$Global{rdtsc_cycle_time} = $1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub report {
|
||||
print "Verilator Gantt report\n";
|
||||
|
||||
print "\nArgument settings:\n";
|
||||
foreach my $arg (sort keys %{$Global{args}}) {
|
||||
my $plus = ($arg =~ /^\+/) ? "+" : " ";
|
||||
printf " %s%s%d\n", $arg, $plus, $Global{args}{$arg};
|
||||
}
|
||||
|
||||
my $nthreads = scalar keys %Threads;
|
||||
$Global{cpus}{cpu_time} = {};
|
||||
foreach my $thread (keys %Threads) {
|
||||
# Make potentially multiple characters per column
|
||||
foreach my $start (keys %{$Threads{$thread}}) {
|
||||
my $cpu = $Threads{$thread}{$start}{cpu};
|
||||
my $elapsed = $Threads{$thread}{$start}{end} - $start;
|
||||
$Global{cpus}{cpu_time}{$cpu} += $elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
my $mt_mtask_time = 0;
|
||||
my $long_mtask_time = 0;
|
||||
my $last_end = 0;
|
||||
foreach my $mtask (keys %Mtasks) {
|
||||
$mt_mtask_time += $Mtasks{$mtask}{elapsed};
|
||||
$last_end = max($last_end, $Mtasks{$mtask}{end});
|
||||
$long_mtask_time = max($long_mtask_time, $Mtasks{$mtask}{elapsed});
|
||||
}
|
||||
$Global{last_end} = $last_end;
|
||||
|
||||
report_graph();
|
||||
|
||||
# If we know cycle time in the same (rdtsc) units,
|
||||
# this will give us an actual utilization number,
|
||||
# (how effectively we keep the cores busy.)
|
||||
#
|
||||
# It also gives us a number we can compare against
|
||||
# serial mode, to estimate the overhead of data sharing,
|
||||
# which will show up in the total elapsed time. (Overhead
|
||||
# of synchronization and scheduling should not.)
|
||||
print "\nAnalysis:\n";
|
||||
printf " Total threads = %d\n", $nthreads;
|
||||
printf " Total mtasks = %d\n", scalar(keys %Mtasks);
|
||||
printf " Total cpus used = %d\n", scalar(keys %{$Global{cpus}});
|
||||
printf " Total yields = %d\n", $Global{stats}{yields};
|
||||
printf " Total eval time = %d rdtsc ticks\n", $Global{last_end};
|
||||
printf " Longest mtask time = %d rdtsc ticks\n", $long_mtask_time;
|
||||
printf " All-thread mtask time = %d rdtsc ticks\n", $mt_mtask_time;
|
||||
my $long_efficiency = $long_mtask_time/($Global{last_end});
|
||||
printf " Longest-thread efficiency = %0.1f%%\n", $long_efficiency*100;
|
||||
my $mt_efficiency = $mt_mtask_time/($Global{last_end}*$nthreads);
|
||||
printf " All-thread efficiency = %0.1f%%\n", $mt_efficiency*100;
|
||||
printf " All-thread speedup = %0.1f\n", $mt_efficiency*$nthreads;
|
||||
if ($Global{rdtsc_cycle_time} > 0) {
|
||||
my $ut = $mt_mtask_time / $Global{rdtsc_cycle_time};
|
||||
print "tot_mtask_cpu=$mt_mtask_time cyc=$Global{rdtsc_cycle_time} ut=$ut\n";
|
||||
}
|
||||
|
||||
my @p2e_ratios;
|
||||
my $min_p2e = 1000000;
|
||||
my $min_mtask;
|
||||
my $max_p2e = -1000000;
|
||||
my $max_mtask;
|
||||
foreach my $mtask (sort keys %Mtasks) {
|
||||
if ($Mtasks{$mtask}{elapsed} > 0) {
|
||||
if ($Mtasks{$mtask}{predict} == 0) {
|
||||
$Mtasks{$mtask}{predict} = 1; # don't log(0) below
|
||||
}
|
||||
my $p2e_ratio = log( $Mtasks{$mtask}{predict} / $Mtasks{$mtask}{elapsed} );
|
||||
#print "log(p2e $mtask) = $p2e_ratio (predict $Mtasks{$mtask}{predict}, elapsed $Mtasks{$mtask}{elapsed})\n";
|
||||
push @p2e_ratios, $p2e_ratio;
|
||||
|
||||
if ($p2e_ratio > $max_p2e) {
|
||||
$max_p2e = $p2e_ratio;
|
||||
$max_mtask = $mtask;
|
||||
}
|
||||
if ($p2e_ratio < $min_p2e) {
|
||||
$min_p2e = $p2e_ratio;
|
||||
$min_mtask = $mtask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print "\nStatistics:\n";
|
||||
print " min log(p2e) = $min_p2e from mtask $min_mtask (predict $Mtasks{$min_mtask}{predict}, elapsed $Mtasks{$min_mtask}{elapsed})\n";
|
||||
print " max log(p2e) = $max_p2e from mtask $max_mtask (predict $Mtasks{$max_mtask}{predict}, elapsed $Mtasks{$max_mtask}{elapsed})\n";
|
||||
|
||||
my $stddev = stddev(\@p2e_ratios);
|
||||
my $mean = mean(\@p2e_ratios);
|
||||
print " mean = " . ($mean) . "\n";
|
||||
print " stddev = " . ($stddev) . "\n";
|
||||
print " e ^ stddev = " . exp($stddev). "\n";
|
||||
print "\n";
|
||||
}
|
||||
|
||||
sub report_graph {
|
||||
my $time_per = $Opt_Time_Per_Char;
|
||||
if ($time_per == 0) {
|
||||
$time_per = ($Global{last_end} / 40); # Start with 40 columns
|
||||
while ($time_per > 10) {
|
||||
my ($graph, $conflicts) = _make_graph($time_per);
|
||||
last if !$conflicts;
|
||||
$time_per = int($time_per/2);
|
||||
}
|
||||
# One more step so we can fit more labels
|
||||
$time_per = int($time_per/2);
|
||||
}
|
||||
|
||||
my ($graph, $conflicts) = _make_graph($time_per);
|
||||
|
||||
print "\nThread gantt graph:\n";
|
||||
print " Legend: One character width = $time_per rdtsc ticks\n";
|
||||
print " Legend: '&' = multiple mtasks in this period (character width)\n";
|
||||
|
||||
my $scale = " <-".$Global{last_end}." rdtsc total";
|
||||
for (my $col = length($scale); # -2 for '->' below
|
||||
$col < ($Global{last_end}/$time_per); ++$col) {
|
||||
$scale .= "-";
|
||||
}
|
||||
print " $scale->\n";
|
||||
|
||||
foreach my $thread (sort keys %{$graph}) {
|
||||
print " t: ";
|
||||
_print_graph_line($graph->{$thread}, '');
|
||||
}
|
||||
}
|
||||
|
||||
sub _make_graph {
|
||||
my $time_per = shift;
|
||||
|
||||
my $graph = {}; # {thread}{column}{char=>'x' or chars=>#}
|
||||
my $conflicts = 0;
|
||||
foreach my $thread (keys %Threads) {
|
||||
# Make potentially multiple characters per column
|
||||
foreach my $start (sort {$a <=> $b} keys %{$Threads{$thread}}) {
|
||||
my $end = $Threads{$thread}{$start}{end};
|
||||
my $mtask = $Threads{$thread}{$start}{mtask};
|
||||
my $cpu = $Threads{$thread}{$start}{cpu};
|
||||
|
||||
my $startcol = _time_col($time_per, $start);
|
||||
my $endcol = _time_col($time_per, $end);
|
||||
|
||||
my $label = "[";
|
||||
$label .= "$cpu"; # Maybe make optional in future
|
||||
my $width = $endcol - $startcol + 1;
|
||||
while (length($label) < ($width-1)) { # -1 for ']'
|
||||
$label .= "-";
|
||||
}
|
||||
$label .= "]";
|
||||
$graph->{$thread}[$startcol]{char} .= $label;
|
||||
}
|
||||
if ($Debug) {
|
||||
print "# Multicol: "; _print_graph_line($graph->{$thread}, '|');
|
||||
}
|
||||
# Expand line to one char per column
|
||||
for (my $col = 0; $col <= $#{$graph->{$thread}}; ++$col) {
|
||||
if (my $chars = $graph->{$thread}[$col]{char}) {
|
||||
my $ok = 1;
|
||||
for (my $coladd = 1; $coladd<length($chars); ++$coladd) {
|
||||
if ($graph->{$thread}[$col + $coladd]{char}) {
|
||||
$ok = 0; last;
|
||||
}
|
||||
}
|
||||
if (!$ok) {
|
||||
if ($chars =~ /\[.*\[/) { # Two begins or more
|
||||
$conflicts++;
|
||||
$graph->{$thread}[$col]{char} = "&";
|
||||
} else {
|
||||
$graph->{$thread}[$col]{char} = "[";
|
||||
}
|
||||
for (my $coladd = 1; $coladd<length($chars); ++$coladd) {
|
||||
if ($graph->{$thread}[$col + $coladd]{char}) {
|
||||
last;
|
||||
} else {
|
||||
$graph->{$thread}[$col + $coladd]{char} = 'x';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
my $coladd = 0;
|
||||
foreach my $char (split //, $chars) {
|
||||
$graph->{$thread}[$col+$coladd]{char} = $char;
|
||||
++$coladd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($Debug) {
|
||||
print "# Singlcol: "; _print_graph_line($graph->{$thread}, '|');
|
||||
}
|
||||
}
|
||||
print "# Conflicts $conflicts\n" if $Debug;
|
||||
return ($graph, $conflicts);
|
||||
}
|
||||
|
||||
sub _print_graph_line {
|
||||
my $graph_thread = shift;
|
||||
my $sep = shift;
|
||||
for (my $col = 0; $col <= $#{$graph_thread}; ++$col) {
|
||||
my $c = $graph_thread->[$col]{char}; $c=' ' if !defined $c;
|
||||
print $c, $sep;
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
sub _time_col {
|
||||
my $time_per = shift;
|
||||
my $time = shift;
|
||||
return int($time/$time_per);
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub write_vcd {
|
||||
my $filename = shift;
|
||||
print "Writing $filename\n";
|
||||
my $fh = IO::File->new(">$filename") or die "%Error: $! $filename,";
|
||||
my $vcd = {values => {}, # {<time>}{<code>} = value
|
||||
sigs => {}, # {<module>}{<sig}} = code
|
||||
code => 0,
|
||||
};
|
||||
|
||||
my %parallelism;
|
||||
foreach my $thread (keys %Threads) {
|
||||
my $mcode = ($vcd->{sigs}{threads}{"thread${thread}_mtask"} ||= $vcd->{code}++);
|
||||
foreach my $start (sort {$a <=> $b} keys %{$Threads{$thread}}) {
|
||||
my $end = $Threads{$thread}{$start}{end};
|
||||
my $mtask = $Threads{$thread}{$start}{mtask};
|
||||
my $cpu = $Threads{$thread}{$start}{cpu};
|
||||
$vcd->{values}{$start}{$mcode} = $mtask;
|
||||
$vcd->{values}{$end}{$mcode} = undef;
|
||||
$parallelism{$start}++;
|
||||
$parallelism{$end}--;
|
||||
|
||||
my $ccode = $vcd->{sigs}{cpus}{"cpu${cpu}_thread"} ||= $vcd->{code}++;
|
||||
$vcd->{values}{$start}{$ccode} = $thread;
|
||||
$vcd->{values}{$end}{$ccode} = undef;
|
||||
|
||||
my $mcode = $vcd->{sigs}{mtasks}{"mtask${mtask}_cpu"} ||= $vcd->{code}++;
|
||||
$vcd->{values}{$start}{$mcode} = $cpu;
|
||||
$vcd->{values}{$end}{$mcode} = undef;
|
||||
}
|
||||
}
|
||||
{
|
||||
my $pcode = ($vcd->{sigs}{Stats}{"parallelism"} ||= $vcd->{code}++);
|
||||
my $value = 0;
|
||||
foreach my $time (sort {$a<=>$b} keys %parallelism) {
|
||||
$value += $parallelism{$time};
|
||||
$vcd->{values}{$time}{$pcode} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$fh->print('$version Generated by verilator_gantt $end'."\n");
|
||||
$fh->print('$timescale 1ns $end'."\n");
|
||||
$fh->print("\n");
|
||||
|
||||
my %all_codes;
|
||||
$fh->print(' $scope module gantt $end'."\n");
|
||||
foreach my $module (sort keys %{$vcd->{sigs}}) {
|
||||
$fh->printf(' $scope module %s $end'."\n", $module);
|
||||
foreach my $sig (sort keys %{$vcd->{sigs}{$module}}) {
|
||||
my $code = $vcd->{sigs}{$module}{$sig};
|
||||
$fh->printf(' $var wire 32 v%x %s [31:0] $end'."\n",
|
||||
$code, $sig);
|
||||
$all_codes{$code} = 1;
|
||||
}
|
||||
$fh->print(' $upscope $end'."\n");
|
||||
}
|
||||
$fh->print(' $upscope $end'."\n");
|
||||
$fh->print('$enddefinitions $end'."\n");
|
||||
$fh->print("\n");
|
||||
|
||||
my $first = 1;
|
||||
foreach my $time (sort {$a <=> $b} keys %{$vcd->{values}}) {
|
||||
if ($first) {
|
||||
$first = 0;
|
||||
# Start with Z for any signals without time zero data
|
||||
foreach my $code (keys %all_codes) {
|
||||
if (!defined $vcd->{values}{$time}{$code}) {
|
||||
$vcd->{values}{$time}{$code} = undef;
|
||||
}
|
||||
}
|
||||
}
|
||||
$fh->printf("#%d\n", $time);
|
||||
foreach my $code (sort keys %{$vcd->{values}{$time}}) {
|
||||
my $value = $vcd->{values}{$time}{$code};
|
||||
if (defined $value) {
|
||||
$fh->printf("b%b v%x\n", $value, $code);
|
||||
} else {
|
||||
$fh->printf("bz v%x\n", $code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
# Similar to Statistics::Basic functions, but avoid a package dependency
|
||||
|
||||
sub max {
|
||||
my $n = $_[0]; shift;
|
||||
while (defined $_[0]) {
|
||||
$n = $_[0] if !defined $n || $_[0] > $n;
|
||||
shift;
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
|
||||
sub mean {
|
||||
my $arrayref = shift;
|
||||
my $n = 0;
|
||||
my $sum = 0;
|
||||
foreach my $v (@$arrayref) {
|
||||
$sum += $v;
|
||||
$n++;
|
||||
}
|
||||
return undef if !$n;
|
||||
return $sum/$n;
|
||||
}
|
||||
|
||||
sub stddev {
|
||||
my $arrayref = shift;
|
||||
my $n = 0;
|
||||
my $sum = 0;
|
||||
my $sumsq = 0;
|
||||
foreach my $v (@$arrayref) {
|
||||
$sum += $v;
|
||||
$sumsq += $v**2;
|
||||
$n++;
|
||||
}
|
||||
return undef if !$n;
|
||||
return sqrt(($sumsq/$n) - ($sum/$n)**2);
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
verilator_gantt - Create Gantt chart of multi-threaded execution
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Creates a visual representation to help analyze Verilator multithreaded
|
||||
simulation performance, by showing when each macro-task starts and ends,
|
||||
and showing when each thread is busy or idle.
|
||||
|
||||
The generated Gantt chart has time on the X-axis. Times shown are to the
|
||||
scale printed, i.e. a certain about of time for each character width. The
|
||||
Y-axis shows threads, each thread's execution is shown on one line. That
|
||||
line shows "[" at the position in time when it executes.
|
||||
|
||||
Following the "[" is the cpu number the task executed on, followed by zero
|
||||
or more "-" to make the width of the characters match the scaled execution
|
||||
time, followed by a "]". If the scale is too small, the cpu number and
|
||||
mtask number will not be printed. If the scale is very small, a "&"
|
||||
indicates multiple mtasks started at that time position.
|
||||
|
||||
Also creates a value change dump (VCD) format dump file which may be viewed
|
||||
in a waveform viewer (e.g. C<GTKWave>). See below.
|
||||
|
||||
=head1 USAGE
|
||||
|
||||
Build with --prof-threads.
|
||||
|
||||
Run a sim with +verilator+prof+threads+window 2.
|
||||
|
||||
This will create profile_threads.dat.
|
||||
|
||||
Then run:
|
||||
|
||||
verilator_gantt profile_threads.dat
|
||||
|
||||
The report will be printed on standard output, this also generates
|
||||
profile_threads.vcd
|
||||
|
||||
View profile_threads.vcd in a waveform viewer.
|
||||
|
||||
=head1 VCD SIGNALS
|
||||
|
||||
In waveforms there are the following signals. Most signals the "decimal"
|
||||
format will remove the leading zeros and make the traces easier to read.
|
||||
|
||||
parallelism: The number of mtasks active at this time, for best performance
|
||||
this will match the thread count. You may want to use an "analog step"
|
||||
format to view this signal.
|
||||
|
||||
cpu#_thread: For the given CPU number, the thread number executing.
|
||||
|
||||
mtask#_cpu; For the given mtask id, the CPU it is executing on.
|
||||
|
||||
thread#_mtask: For the given thread number, the mtask id executing.
|
||||
|
||||
=head1 ARGUMENTS
|
||||
|
||||
=over 4
|
||||
|
||||
=item I<filename>
|
||||
|
||||
The filename to read data from, defaults to "profile_threads.dat".
|
||||
|
||||
=item --help
|
||||
|
||||
Displays this message and program version and exits.
|
||||
|
||||
=item --scale I<n>
|
||||
|
||||
On the X-axis of the generated Gantt chart, each character represents this
|
||||
many time units. (On x86, time units are rdtsc ticks.) Defaults to 0,
|
||||
which will automatically compute a reasonable scale where no two mtasks
|
||||
need to fit into same character width's worth of scaled time.
|
||||
|
||||
=item --no-vcd
|
||||
|
||||
=item --vcd I<filename>
|
||||
|
||||
Set output filename for vcd dump, or disable. Default is
|
||||
verilator_gantt.vcd.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
The latest version is available from L<http://www.veripool.org/>.
|
||||
|
||||
Copyright 2018-2018 by Wilson Snyder. Verilator 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.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Wilson Snyder <wsnyder@wsnyder.org>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
C<verilator>
|
||||
|
||||
=cut
|
||||
|
||||
######################################################################
|
||||
### Local Variables:
|
||||
### compile-command: "$V4/bin/verilator_gantt $V4/test_regress/obj_vltmt/t_gantt/vlt_sim.log"
|
||||
### End:
|
||||
38
configure.ac
38
configure.ac
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[3.927 devel],[https://www.veripool.org/verilator],
|
||||
AC_INIT([Verilator],[4.000 devel],[https://www.veripool.org/verilator],
|
||||
[verilator],[https://www.veripool.org/verilator])
|
||||
# When releasing, also update header of Changes file
|
||||
# and commit using "devel release" or "Version bump" message
|
||||
|
|
@ -189,6 +189,34 @@ AC_DEFUN([_MY_CXX_CHECK_OPT],
|
|||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN([_MY_LDLIBS_CHECK_FLAG],
|
||||
[# _MY_LDLIBS_CHECK_FLAG(flag) -- Check if linker supports specific options
|
||||
# Set $_my_result appropriately
|
||||
ACO_SAVE_LIBS="$LIBS"
|
||||
LIBS="$LIBS $1"
|
||||
AC_MSG_CHECKING([whether $CXX linker accepts $1])
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([[]])],
|
||||
[_my_result=yes
|
||||
if test -s conftest.err; then
|
||||
if grep -e "$1" conftest.err >/dev/null; then
|
||||
_my_result=no
|
||||
fi
|
||||
fi],
|
||||
[_my_result=no])
|
||||
AC_MSG_RESULT($_my_result)
|
||||
LIBS="$ACO_SAVE_LIBS"
|
||||
])
|
||||
|
||||
AC_DEFUN([_MY_LDLIBS_CHECK_OPT],
|
||||
[# _MY_LDLIBS_CHECK_OPT(flag) -- Check if linker supports specific options
|
||||
# If it does, append flag to variable
|
||||
_MY_LDLIBS_CHECK_FLAG($2)
|
||||
if test "$_my_result" = "yes" ; then
|
||||
$1="$$1 $2"
|
||||
fi
|
||||
])
|
||||
|
||||
# Flag to select newest language standard supported
|
||||
# Macros work such that first option that passes is the one we take
|
||||
# gnu++14 is the newest that Verilator supports
|
||||
|
|
@ -225,7 +253,6 @@ AC_SUBST(CFG_CXXFLAGS_WEXTRA)
|
|||
# Flags for compiling Verilator internals including parser always
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Qunused-arguments)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-faligned-new)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-delete-null-pointer-checks)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-unused-parameter)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-undefined-bool-conversion)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-shadow)
|
||||
|
|
@ -257,6 +284,13 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Wno-unused-variable)
|
|||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,-Wno-shadow)
|
||||
AC_SUBST(CFG_CXXFLAGS_NO_UNUSED)
|
||||
|
||||
# Find multithread linker flags
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-mt)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-pthread)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-lpthread)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_THREADS,-latomic)
|
||||
AC_SUBST(CFG_LDLIBS_THREADS)
|
||||
|
||||
# Set CFG_WITH_THREADED if can support threading
|
||||
AC_MSG_CHECKING(whether $CXX supports Verilated threads)
|
||||
ACO_SAVE_CXXFLAGS="$CXXFLAGS"
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ VerilatedVoidCb Verilated::s_flushCb = NULL;
|
|||
|
||||
// Keep below together in one cache line
|
||||
Verilated::Serialized Verilated::s_s;
|
||||
Verilated::NonSerialized Verilated::s_ns;
|
||||
VL_THREAD_LOCAL Verilated::ThreadLocal Verilated::t_s;
|
||||
|
||||
Verilated::CommandArgValues Verilated::s_args;
|
||||
|
|
@ -196,6 +197,17 @@ Verilated::Serialized::Serialized() {
|
|||
s_fatalOnVpiError = true; // retains old default behaviour
|
||||
}
|
||||
|
||||
Verilated::NonSerialized::NonSerialized() {
|
||||
s_profThreadsStart = 1;
|
||||
s_profThreadsWindow = 2;
|
||||
s_profThreadsFilenamep = strdup("profile_threads.dat");
|
||||
}
|
||||
Verilated::NonSerialized::~NonSerialized() {
|
||||
if (s_profThreadsFilenamep) {
|
||||
free(const_cast<char*>(s_profThreadsFilenamep)); s_profThreadsFilenamep=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Random reset -- Only called at init time, so don't inline.
|
||||
|
||||
|
|
@ -1648,6 +1660,20 @@ void Verilated::fatalOnVpiError(bool flag) VL_MT_SAFE {
|
|||
VerilatedLockGuard lock(m_mutex);
|
||||
s_s.s_fatalOnVpiError = flag;
|
||||
}
|
||||
void Verilated::profThreadsStart(vluint64_t flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
s_ns.s_profThreadsStart = flag;
|
||||
}
|
||||
void Verilated::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
s_ns.s_profThreadsWindow = flag;
|
||||
}
|
||||
void Verilated::profThreadsFilenamep(const char* flagp) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
if (s_ns.s_profThreadsFilenamep) free(const_cast<char*>(s_ns.s_profThreadsFilenamep));
|
||||
s_ns.s_profThreadsFilenamep = strdup(flagp);
|
||||
}
|
||||
|
||||
|
||||
const char* Verilated::catName(const char* n1, const char* n2) VL_MT_SAFE {
|
||||
// Returns new'ed data
|
||||
|
|
@ -1684,7 +1710,7 @@ void Verilated::flushCall() VL_MT_SAFE {
|
|||
}
|
||||
|
||||
void Verilated::commandArgs(int argc, const char** argv) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
VerilatedLockGuard lock(s_args.m_argMutex);
|
||||
s_args.argc = argc;
|
||||
s_args.argv = argv;
|
||||
VerilatedImp::commandArgs(argc,argv);
|
||||
|
|
@ -1780,9 +1806,61 @@ void VerilatedImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s
|
|||
if (!s_s.m_argVecLoaded) s_s.m_argVec.clear();
|
||||
for (int i=0; i<argc; ++i) {
|
||||
s_s.m_argVec.push_back(argv[i]);
|
||||
commandArgVl(argv[i]);
|
||||
}
|
||||
s_s.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
|
||||
}
|
||||
void VerilatedImp::commandArgVl(const std::string& arg) {
|
||||
if (0 == strncmp(arg.c_str(), "+verilator+", strlen("+verilator+"))) {
|
||||
std::string value;
|
||||
if (0) {
|
||||
}
|
||||
else if (arg == "+verilator+debug") {
|
||||
Verilated::debug(4);
|
||||
}
|
||||
else if (commandArgVlValue(arg, "+verilator+debugi+", value/*ref*/)) {
|
||||
Verilated::debug(atoi(value.c_str()));
|
||||
}
|
||||
else if (arg == "+verilator+help") {
|
||||
versionDump();
|
||||
VL_PRINTF_MT("For help, please see 'verilator --help'\n");
|
||||
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
|
||||
}
|
||||
else if (commandArgVlValue(arg, "+verilator+prof+threads+start+", value/*ref*/)) {
|
||||
Verilated::profThreadsStart(atoll(value.c_str()));
|
||||
}
|
||||
else if (commandArgVlValue(arg, "+verilator+prof+threads+window+", value/*ref*/)) {
|
||||
Verilated::profThreadsWindow(atol(value.c_str()));
|
||||
}
|
||||
else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value/*ref*/)) {
|
||||
Verilated::profThreadsFilenamep(value.c_str());
|
||||
}
|
||||
else if (commandArgVlValue(arg, "+verilator+rand+reset+", value/*ref*/)) {
|
||||
Verilated::randReset(atoi(value.c_str()));
|
||||
}
|
||||
else if (arg == "+verilator+V") {
|
||||
versionDump(); // Someday more info too
|
||||
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
|
||||
}
|
||||
else if (arg == "+verilator+version") {
|
||||
versionDump();
|
||||
VL_FATAL_MT("COMMAND_LINE", 0, "", "Exiting due to command line argument (not an error)");
|
||||
}
|
||||
else {
|
||||
VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
bool VerilatedImp::commandArgVlValue(const std::string& arg,
|
||||
const std::string& prefix, std::string& valuer) {
|
||||
size_t len = prefix.length();
|
||||
if (0==strncmp(prefix.c_str(), arg.c_str(), len)) {
|
||||
valuer = arg.substr(len);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedSyms:: Methods
|
||||
|
|
|
|||
|
|
@ -344,9 +344,21 @@ class Verilated {
|
|||
~Serialized() {}
|
||||
} s_s;
|
||||
|
||||
static struct NonSerialized { // Non-serialized information
|
||||
// These are reloaded from on command-line settings, so do not need to persist
|
||||
// Fast path
|
||||
vluint64_t s_profThreadsStart; ///< +prof+threads starting time
|
||||
vluint32_t s_profThreadsWindow; ///< +prof+threads window size
|
||||
// Slow path
|
||||
const char* s_profThreadsFilenamep; ///< +prof+threads filename
|
||||
NonSerialized();
|
||||
~NonSerialized();
|
||||
} s_ns;
|
||||
|
||||
// no need to be save-restored (serialized) the
|
||||
// assumption is that the restore is allowed to pass different arguments
|
||||
static struct CommandArgValues {
|
||||
VerilatedMutex m_argMutex; ///< Mutex for s_args members, when VL_THREADED
|
||||
int argc;
|
||||
const char** argv;
|
||||
CommandArgValues() : argc(0), argv(NULL) {}
|
||||
|
|
@ -408,6 +420,14 @@ public:
|
|||
/// Enable/disable vpi fatal
|
||||
static void fatalOnVpiError(bool flag) VL_MT_SAFE;
|
||||
static bool fatalOnVpiError() VL_MT_SAFE { return s_s.s_fatalOnVpiError; }
|
||||
/// --prof-threads related settings
|
||||
static void profThreadsStart(vluint64_t flag) VL_MT_SAFE;
|
||||
static vluint64_t profThreadsStart() VL_MT_SAFE { return s_ns.s_profThreadsStart; }
|
||||
static void profThreadsWindow(vluint64_t flag) VL_MT_SAFE;
|
||||
static vluint32_t profThreadsWindow() VL_MT_SAFE { return s_ns.s_profThreadsWindow; }
|
||||
static void profThreadsFilenamep(const char* flagp) VL_MT_SAFE;
|
||||
static const char* profThreadsFilenamep() VL_MT_SAFE { return s_ns.s_profThreadsFilenamep; }
|
||||
|
||||
/// Flush callback for VCD waves
|
||||
static void flushCb(VerilatedVoidCb cb) VL_MT_SAFE;
|
||||
static void flushCall() VL_MT_SAFE;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ CFG_CXXFLAGS_STD_OLDEST = @CFG_CXXFLAGS_STD_OLDEST@
|
|||
CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
|
||||
# Compiler flags that turn on extra warnings
|
||||
CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
|
||||
# Linker libraries for multithreading
|
||||
CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
|
||||
|
||||
######################################################################
|
||||
# Programs
|
||||
|
|
@ -116,6 +118,7 @@ ifneq ($(VM_THREADS),0)
|
|||
ifneq ($(VM_THREADS),)
|
||||
# Need C++11 at least, so always default to newest
|
||||
CPPFLAGS += -DVL_THREADED $(CFG_CXXFLAGS_STD_NEWEST)
|
||||
LDLIBS += $(CFG_LDLIBS_THREADS)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -239,6 +239,9 @@ public:
|
|||
}
|
||||
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);
|
||||
|
||||
public:
|
||||
// METHODS - user scope tracking
|
||||
|
|
|
|||
|
|
@ -0,0 +1,229 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2012-2018 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.
|
||||
//
|
||||
// This is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Thread pool for verilated modules
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_threads.h"
|
||||
#include <cstdio>
|
||||
|
||||
std::atomic<vluint64_t> VlNotification::s_yields;
|
||||
|
||||
VL_THREAD_LOCAL VlThreadPool::ProfileTrace* VlThreadPool::t_profilep = NULL;
|
||||
|
||||
//=============================================================================
|
||||
// VlMTaskVertex
|
||||
|
||||
VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
|
||||
: m_upstreamDepsDone(0),
|
||||
m_upstreamDepCount(upstreamDepCount) {
|
||||
assert(atomic_is_lock_free(&m_upstreamDepsDone));
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlWorkerThread
|
||||
|
||||
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:
|
||||
, m_cthread(startWorker, this) {}
|
||||
|
||||
VlWorkerThread::~VlWorkerThread() {
|
||||
m_exiting.store(true, std::memory_order_release);
|
||||
{
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
if (sleeping()) {
|
||||
wakeUp();
|
||||
}
|
||||
}
|
||||
// The thread should exit; join it.
|
||||
m_cthread.join();
|
||||
}
|
||||
|
||||
void VlWorkerThread::workerLoop() {
|
||||
if (VL_UNLIKELY(m_profiling)) {
|
||||
m_poolp->setupProfilingClientThread();
|
||||
}
|
||||
|
||||
VlNotification alarm;
|
||||
ExecRec work;
|
||||
work.m_fnp = NULL;
|
||||
|
||||
while (1) {
|
||||
bool sleep = false;
|
||||
if (VL_UNLIKELY(!work.m_fnp)) {
|
||||
// Look for work
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
if (VL_LIKELY(!m_ready.empty())) {
|
||||
dequeWork(&work);
|
||||
} else {
|
||||
// No work available, prepare to sleep. Pass alarm/work
|
||||
// into m_sleepAlarm so wakeUp will tall this function.
|
||||
//
|
||||
// Must modify m_sleepAlarm in the same critical section as
|
||||
// the check for ready work, otherwise we could race with
|
||||
// another thread enqueueing work and never be awoken.
|
||||
m_sleepAlarm.first = &alarm;
|
||||
m_sleepAlarm.second = &work;
|
||||
sleep = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 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(sleep)) {
|
||||
alarm.waitForNotification(); // ZZZzzzzz
|
||||
alarm.reset();
|
||||
}
|
||||
if (VL_LIKELY(work.m_fnp)) {
|
||||
work.m_fnp(work.m_evenCycle, work.m_sym);
|
||||
work.m_fnp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(m_profiling)) {
|
||||
m_poolp->tearDownProfilingClientThread();
|
||||
}
|
||||
}
|
||||
|
||||
void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
|
||||
workerp->workerLoop();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlThreadPool
|
||||
|
||||
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) {
|
||||
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
|
||||
" --threads %d; may run slow.\n", cpus, nThreads+1);
|
||||
}
|
||||
// Create'em
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
VlThreadPool::~VlThreadPool() {
|
||||
for (int i = 0; i < m_workers.size(); ++i) {
|
||||
// Each ~WorkerThread will wait for its thread to exit.
|
||||
delete m_workers[i];
|
||||
}
|
||||
if (VL_UNLIKELY(m_profiling)) {
|
||||
tearDownProfilingClientThread();
|
||||
}
|
||||
}
|
||||
|
||||
void VlThreadPool::tearDownProfilingClientThread() {
|
||||
assert(t_profilep);
|
||||
delete t_profilep;
|
||||
t_profilep = NULL;
|
||||
}
|
||||
|
||||
void VlThreadPool::setupProfilingClientThread() {
|
||||
assert(!t_profilep);
|
||||
t_profilep = new ProfileTrace;
|
||||
// Reserve some space in the thread-local profiling buffer;
|
||||
// try not to malloc while collecting profiling.
|
||||
t_profilep->reserve(4096);
|
||||
{
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
m_allProfiles.insert(t_profilep);
|
||||
}
|
||||
}
|
||||
|
||||
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) {
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) {
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
|
||||
|
||||
FILE* fp = fopen(filenamep, "w");
|
||||
if (VL_UNLIKELY(!fp)) {
|
||||
VL_FATAL_MT(filenamep, 0, "", "+prof+threads+file file not writable");
|
||||
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 +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",
|
||||
VlNotification::yields());
|
||||
|
||||
vluint32_t thread_id = 0;
|
||||
for (ProfileSet::iterator pit = m_allProfiles.begin();
|
||||
pit != m_allProfiles.end(); ++pit) {
|
||||
++thread_id;
|
||||
|
||||
bool printing = false; // False while in warmup phase
|
||||
for (ProfileTrace::iterator eit = (*pit)->begin();
|
||||
eit != (*pit)->end(); ++eit) {
|
||||
switch (eit->m_type) {
|
||||
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"
|
||||
" 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,
|
||||
thread_id);
|
||||
break;
|
||||
default: assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n",
|
||||
ticksElapsed);
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
|
@ -0,0 +1,313 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2012-2018 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.
|
||||
//
|
||||
// This is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Thread pool and profiling for Verilated modules
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#ifndef _VERILATED_THREADS_H_
|
||||
#define _VERILATED_THREADS_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <sched.h> // For sched_getcpu()
|
||||
|
||||
#include "verilated.h" // for VerilatedMutex and clang annotations
|
||||
|
||||
// VlMTaskVertex and VlThreadpool will work with multiple symbol table types.
|
||||
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
|
||||
// as a void* here.
|
||||
typedef void* VlThrSymTab;
|
||||
|
||||
class VlNotification {
|
||||
// MEMBERS
|
||||
std::atomic<bool> m_notified; // Notification pending
|
||||
static std::atomic<vluint64_t> s_yields; // Statistics
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlNotification()
|
||||
: m_notified(false) {
|
||||
assert(atomic_is_lock_free(&m_notified));
|
||||
}
|
||||
~VlNotification() {}
|
||||
|
||||
// METHODS
|
||||
static vluint64_t yields() { return s_yields; }
|
||||
|
||||
// Block until notify() has occurred, then return.
|
||||
// If notify() has already occurred, return immediately.
|
||||
//
|
||||
// This is logically const: the object will remain in notified state
|
||||
// after WaitForNotification() returns, so you could notify more than
|
||||
// one thread of the same event.
|
||||
inline void waitForNotification() {
|
||||
unsigned ct = 0;
|
||||
while (VL_UNLIKELY(!notified())) {
|
||||
VL_CPU_RELAX();
|
||||
ct++;
|
||||
if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) {
|
||||
ct = 0;
|
||||
++s_yields; // Statistics
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The 'inline' keyword here means nothing to the compiler, it's
|
||||
// implicit on methods defined within the class body anyway.
|
||||
//
|
||||
// 'inline' is attached the this method, and others in this file,
|
||||
// to remind humans that some routines in this file are called many
|
||||
// times per cycle in threaded mode. Such routines should be
|
||||
// inlinable; that's why they're declared in the .h and not the .cpp.
|
||||
inline bool notified() {
|
||||
return m_notified.load(std::memory_order_acquire);
|
||||
}
|
||||
// Set notified state. If state is already notified,
|
||||
// it remains so.
|
||||
inline void notify() {
|
||||
m_notified.store(true, std::memory_order_release);
|
||||
}
|
||||
// Reset the state to un-notified state, which is also the
|
||||
// state of a new Notification object.
|
||||
inline void reset() {
|
||||
m_notified.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
};
|
||||
|
||||
typedef void (*VlExecFnp)(bool, VlThrSymTab);
|
||||
|
||||
/// Track dependencies for a single MTask.
|
||||
class VlMTaskVertex {
|
||||
// MEMBERS
|
||||
|
||||
// On even cycles, _upstreamDepsDone increases as upstream
|
||||
// dependencies complete. When it reaches _upstreamDepCount,
|
||||
// this MTaskVertex is ready.
|
||||
//
|
||||
// On odd cycles, _upstreamDepsDone decreases as upstream
|
||||
// dependencies complete, and when it reaches zero this MTaskVertex
|
||||
// is ready.
|
||||
//
|
||||
// An atomic is smaller than a mutex, and lock-free.
|
||||
//
|
||||
// (Why does the size of this class matter? If an mtask has many
|
||||
// downstream mtasks to notify, we hope these will pack into a
|
||||
// small number of cache lines to reduce the cost of pointer chasing
|
||||
// during done-notification. Nobody's quantified that cost though.
|
||||
// If we were really serious about shrinking this class, we could
|
||||
// use 16-bit types here...)
|
||||
std::atomic<vluint32_t> m_upstreamDepsDone;
|
||||
const vluint32_t m_upstreamDepCount;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
||||
// 'upstreamDepCount' is the number of upstream MTaskVertex's
|
||||
// that must notify this MTaskVertex before it will become ready
|
||||
// to run.
|
||||
explicit VlMTaskVertex(vluint32_t upstreamDepCount);
|
||||
~VlMTaskVertex() {}
|
||||
|
||||
// Upstream mtasks must call this when they complete.
|
||||
// Returns true when the current MTaskVertex becomes ready to execute,
|
||||
// false while it's still waiting on more dependencies.
|
||||
inline bool signalUpstreamDone(bool evenCycle) {
|
||||
if (evenCycle) {
|
||||
vluint32_t upstreamDepsDone
|
||||
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
|
||||
assert(upstreamDepsDone <= m_upstreamDepCount);
|
||||
return (upstreamDepsDone == m_upstreamDepCount);
|
||||
} else {
|
||||
vluint32_t upstreamDepsDone_prev
|
||||
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
|
||||
assert(upstreamDepsDone_prev > 0);
|
||||
return (upstreamDepsDone_prev == 1);
|
||||
}
|
||||
}
|
||||
inline bool areUpstreamDepsDone(bool evenCycle) const {
|
||||
vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
|
||||
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
|
||||
}
|
||||
inline void waitUntilUpstreamDone(bool evenCycle) const {
|
||||
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Profiling support
|
||||
class VlProfileRec {
|
||||
protected:
|
||||
friend class VlThreadPool;
|
||||
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
|
||||
vluint64_t m_startTime; // Tick at start of execution
|
||||
vluint64_t m_endTime; // Tick at end of execution
|
||||
unsigned m_cpu; // Execution CPU number (at start anyways)
|
||||
public:
|
||||
class Barrier {};
|
||||
VlProfileRec() {}
|
||||
explicit VlProfileRec(Barrier) {
|
||||
m_type = TYPE_BARRIER;
|
||||
m_mtaskId = 0;
|
||||
m_predictTime = 0;
|
||||
m_startTime = 0;
|
||||
m_cpu = sched_getcpu();
|
||||
}
|
||||
void startRecord(vluint64_t time, uint32_t mtask, uint32_t predict) {
|
||||
m_type = VlProfileRec::TYPE_MTASK_RUN;
|
||||
m_mtaskId = mtask;
|
||||
m_predictTime = predict;
|
||||
m_startTime = time;
|
||||
m_cpu = sched_getcpu();
|
||||
}
|
||||
void endRecord(vluint64_t time) {
|
||||
m_endTime = time;
|
||||
}
|
||||
};
|
||||
|
||||
class VlThreadPool;
|
||||
|
||||
class VlWorkerThread {
|
||||
private:
|
||||
// TYPES
|
||||
struct ExecRec {
|
||||
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(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym)
|
||||
: m_fnp(fnp), m_sym(sym), m_evenCycle(evenCycle) {}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
VerilatedMutex m_mutex;
|
||||
|
||||
// Why a vector? We expect the pending list to be very short, typically
|
||||
// 0 or 1 or 2, so popping from the front shouldn't be
|
||||
// expensive. Revisit if we ever have longer queues...
|
||||
std::vector<ExecRec> m_ready VL_GUARDED_BY(m_mutex);
|
||||
|
||||
VlThreadPool* m_poolp; // Our associated thread pool
|
||||
|
||||
// If values stored are non-NULL, the thread is asleep pending new
|
||||
// work. If the thread is not asleep, both parts of m_sleepAlarm must
|
||||
// be NULL.
|
||||
std::pair<VlNotification*, ExecRec*> m_sleepAlarm VL_GUARDED_BY(m_mutex);
|
||||
|
||||
bool m_profiling; // Is profiling enabled?
|
||||
std::atomic<bool> m_exiting; // Worker thread should exit
|
||||
std::thread m_cthread; // Underlying C++ thread record
|
||||
|
||||
VL_UNCOPYABLE(VlWorkerThread);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit VlWorkerThread(VlThreadPool* poolp, bool profiling);
|
||||
~VlWorkerThread();
|
||||
|
||||
// METHODS
|
||||
inline void dequeWork(ExecRec* workp) VL_REQUIRES(m_mutex) {
|
||||
// As noted above this is inefficient if our ready list is ever
|
||||
// long (but it shouldn't be)
|
||||
*workp = m_ready.front();
|
||||
m_ready.erase(m_ready.begin());
|
||||
}
|
||||
inline void wakeUp() VL_REQUIRES(m_mutex) {
|
||||
VlNotification* notifyp = m_sleepAlarm.first;
|
||||
m_sleepAlarm.first = NULL; // NULL+NULL means wake
|
||||
m_sleepAlarm.second = NULL;
|
||||
notifyp->notify();
|
||||
}
|
||||
inline bool sleeping() VL_REQUIRES(m_mutex) {
|
||||
return (m_sleepAlarm.first != NULL);
|
||||
}
|
||||
inline void addTask(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym) {
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
m_ready.emplace_back(fnp, evenCycle, sym);
|
||||
if (VL_LIKELY(sleeping())) { // Generally queue is waiting for work
|
||||
// Awaken thread
|
||||
dequeWork(m_sleepAlarm.second);
|
||||
wakeUp();
|
||||
}
|
||||
}
|
||||
void workerLoop();
|
||||
static void startWorker(VlWorkerThread* workerp);
|
||||
};
|
||||
|
||||
class VlThreadPool {
|
||||
// TYPES
|
||||
typedef std::vector<VlProfileRec> ProfileTrace;
|
||||
typedef std::set<ProfileTrace*> ProfileSet;
|
||||
|
||||
// MEMBERS
|
||||
std::vector<VlWorkerThread*> m_workers; // our workers
|
||||
bool m_profiling; // is profiling enabled?
|
||||
|
||||
// Support profiling -- we can append records of profiling events
|
||||
// to this vector with very low overhead, and then dump them out
|
||||
// later. This prevents the overhead of printf/malloc/IO from
|
||||
// corrupting the profiling data. It's super cheap to append
|
||||
// a VlProfileRec struct on the end of a pre-allocated vector;
|
||||
// this is the only cost we pay in real-time during a profiling cycle.
|
||||
static VL_THREAD_LOCAL ProfileTrace* t_profilep;
|
||||
ProfileSet m_allProfiles VL_GUARDED_BY(m_mutex);
|
||||
VerilatedMutex m_mutex;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// Construct a thread pool with 'nThreads' dedicated threads. The thread
|
||||
// pool will create these threads and make them available to execute tasks
|
||||
// via this->workerp(index)->addTask(...)
|
||||
VlThreadPool(int nThreads, bool profiling);
|
||||
~VlThreadPool();
|
||||
|
||||
// METHODS
|
||||
inline int numThreads() const {
|
||||
return m_workers.size();
|
||||
}
|
||||
inline VlWorkerThread* workerp(int index) {
|
||||
assert(index >= 0);
|
||||
assert(index < m_workers.size());
|
||||
return m_workers[index];
|
||||
}
|
||||
inline VlProfileRec* profileAppend() {
|
||||
t_profilep->emplace_back();
|
||||
return &(t_profilep->back());
|
||||
}
|
||||
void profileAppendAll(const VlProfileRec& rec);
|
||||
void profileDump(const char* filenamep, vluint64_t ticksElapsed);
|
||||
// In profiling mode, each executing thread must call
|
||||
// this once to setup profiling state:
|
||||
void setupProfilingClientThread();
|
||||
void tearDownProfilingClientThread();
|
||||
private:
|
||||
VL_UNCOPYABLE(VlThreadPool);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,548 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: pre-C++11 replacements for std::unordered_set
|
||||
// and std::unordered_map.
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2018 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
|
||||
//*************************************************************************
|
||||
// This file has clones of the std::unordered_set and std::unordered_map
|
||||
// hash table types. They are here so that Verilator can use hash tables
|
||||
// in pre-C++11 compilers, and the same client code can link against the
|
||||
// std:: types when they are available.
|
||||
//
|
||||
// The implementations in this file do not implement the complete APIs
|
||||
// of the std:: types. Nor are they correct in every detail,
|
||||
// notably, the const_iterators do not enforce constness. We can extend
|
||||
// these implementations to cover more of the std API as needed.
|
||||
//
|
||||
// TODO: In the future, when Verilator requires C++11 to compile,
|
||||
// remove this entire file and switch to the std:: types.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _V3_UNORDERED_SET_MAP_H_
|
||||
#define _V3_UNORDERED_SET_MAP_H_
|
||||
|
||||
#include "verilated_config.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include <list>
|
||||
#include <stdexcept>
|
||||
#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_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!
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
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 {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
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 {
|
||||
// Don't scan the strings if the sizes are different.
|
||||
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)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct vl_equal_to<T*> {
|
||||
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> >
|
||||
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;
|
||||
|
||||
class iterator {
|
||||
protected:
|
||||
// MEMBERS
|
||||
size_t m_bucketIdx; // Bucket this iterator points into.
|
||||
typename Bucket::iterator m_bit; // Bucket-local iterator.
|
||||
const vl_unordered_set* m_setp; // The containing set.
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
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;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
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));
|
||||
}
|
||||
void advanceUntilValid() {
|
||||
while (1) {
|
||||
if (m_bit != m_setp->m_bucketsp[m_bucketIdx].end()) {
|
||||
// Valid iterator in this bucket; we're done.
|
||||
return;
|
||||
}
|
||||
|
||||
// Try the next bucket?
|
||||
m_bucketIdx++;
|
||||
if (m_bucketIdx == m_setp->numBuckets()) {
|
||||
// Ran past the end of buckets, set to end().
|
||||
*this = m_setp->end();
|
||||
return;
|
||||
}
|
||||
m_bit = m_setp->m_bucketsp[m_bucketIdx].begin();
|
||||
}
|
||||
}
|
||||
void operator++() {
|
||||
++m_bit;
|
||||
advanceUntilValid();
|
||||
}
|
||||
|
||||
typename Bucket::iterator bit() const { return m_bit; }
|
||||
};
|
||||
|
||||
// TODO: there's no real const enforcement on the 'const_iterator'.
|
||||
typedef iterator const_iterator;
|
||||
|
||||
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;
|
||||
// // 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.
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
vl_unordered_set()
|
||||
: m_numElements(0)
|
||||
, m_log2Buckets(initLog2Buckets())
|
||||
, m_bucketsp(NULL)
|
||||
, m_hash()
|
||||
, m_equal() { }
|
||||
|
||||
vl_unordered_set(const vl_unordered_set& other)
|
||||
: m_numElements(other.m_numElements)
|
||||
, m_log2Buckets(other.m_log2Buckets)
|
||||
, m_bucketsp(NULL)
|
||||
, m_hash()
|
||||
, 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
~vl_unordered_set() {
|
||||
delete [] m_bucketsp; VL_DANGLING(m_bucketsp);
|
||||
}
|
||||
|
||||
vl_unordered_set& operator=(const vl_unordered_set& other) {
|
||||
if (this != &other) {
|
||||
clear();
|
||||
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];
|
||||
}
|
||||
} else {
|
||||
m_bucketsp = NULL;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
static size_t initLog2Buckets() { return 4; }
|
||||
|
||||
iterator begin() {
|
||||
if (m_numElements) {
|
||||
initBuckets();
|
||||
iterator result = iterator(0, m_bucketsp[0].begin(), this);
|
||||
result.advanceUntilValid();
|
||||
return result;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
const_iterator begin() const {
|
||||
if (m_numElements) {
|
||||
initBuckets();
|
||||
const_iterator result = iterator(0, m_bucketsp[0].begin(), this);
|
||||
result.advanceUntilValid();
|
||||
return result;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
const_iterator end() const {
|
||||
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 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/
|
||||
//
|
||||
// * The magic numbers below are UINT_MAX/phi where phi is the
|
||||
// golden ratio number (1.618...) for either 64- or 32-bit
|
||||
// values of UINT_MAX.
|
||||
//
|
||||
// * Fibonacci hashing mixes the result of the client's hash
|
||||
// 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));
|
||||
return result;
|
||||
}
|
||||
|
||||
iterator find_internal(const T_Key& key, size_t& bucketIdxOut) {
|
||||
size_t hash = m_hash.operator()(key);
|
||||
bucketIdxOut = hashToBucket(hash);
|
||||
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);
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
const_iterator find(const T_Key& key) const {
|
||||
size_t bucketIdx;
|
||||
return const_cast<vl_unordered_set*>(this)->find_internal(key,
|
||||
bucketIdx);
|
||||
}
|
||||
|
||||
iterator find(const T_Key& key) {
|
||||
size_t bucketIdx;
|
||||
return find_internal(key, bucketIdx);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const T_Key &val) {
|
||||
size_t bucketIdx;
|
||||
iterator existIt = find_internal(val, bucketIdx);
|
||||
if (existIt != end()) {
|
||||
// Collision with existing element.
|
||||
//
|
||||
// An element may be inserted only if it is not
|
||||
// equal to an existing element. So fail.
|
||||
return std::pair<iterator, bool>(end(), false);
|
||||
}
|
||||
|
||||
// No collision, so insert it.
|
||||
m_numElements++;
|
||||
|
||||
m_bucketsp[bucketIdx].push_front(val);
|
||||
|
||||
// Compute result iterator. This pointer will be valid
|
||||
// if we don't rehash:
|
||||
iterator result_it(bucketIdx, m_bucketsp[bucketIdx].begin(), this);
|
||||
|
||||
if (needToRehash(GROW)) {
|
||||
rehash(GROW);
|
||||
// ... since we rehashed, do a lookup to get the result iterator.
|
||||
result_it = find(val);
|
||||
}
|
||||
|
||||
return std::pair<iterator, bool>(result_it, true);
|
||||
}
|
||||
|
||||
iterator erase(iterator it) {
|
||||
iterator next_it = it;
|
||||
++next_it;
|
||||
erase(*it);
|
||||
return next_it;
|
||||
}
|
||||
|
||||
size_t erase(const T_Key &key) {
|
||||
size_t bucketIdx;
|
||||
iterator it = find_internal(key, bucketIdx);
|
||||
if (it != end()) {
|
||||
m_bucketsp[bucketIdx].erase(it.bit());
|
||||
m_numElements--;
|
||||
// Rehashing to handle a shrinking data set is important
|
||||
// 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);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (m_bucketsp) {
|
||||
delete [] m_bucketsp;
|
||||
m_bucketsp = NULL;
|
||||
}
|
||||
m_numElements = 0;
|
||||
m_log2Buckets = initLog2Buckets();
|
||||
}
|
||||
|
||||
private:
|
||||
size_t numBuckets() const { return (VL_ULL(1) << m_log2Buckets); }
|
||||
|
||||
Bucket* getBucket(size_t idx) {
|
||||
initBuckets();
|
||||
return &m_bucketsp[idx];
|
||||
}
|
||||
|
||||
void initBuckets() const {
|
||||
if (!m_bucketsp) m_bucketsp = new Bucket[numBuckets()];
|
||||
}
|
||||
|
||||
bool needToRehash(RehashType rt) const {
|
||||
if (rt == GROW) {
|
||||
return ((4 * numBuckets()) < m_numElements);
|
||||
} else {
|
||||
return (numBuckets() > (4 * m_numElements));
|
||||
}
|
||||
}
|
||||
|
||||
void rehash(RehashType rt) {
|
||||
size_t new_log2Buckets;
|
||||
if (rt == GROW) {
|
||||
new_log2Buckets = m_log2Buckets + 2;
|
||||
} else {
|
||||
if (m_log2Buckets <= 4) {
|
||||
// On shrink, saturate m_log2Buckets at its
|
||||
// initial size of 2^4 == 16 buckets. Don't risk
|
||||
// underflowing!
|
||||
return;
|
||||
}
|
||||
new_log2Buckets = m_log2Buckets - 2;
|
||||
}
|
||||
|
||||
size_t new_num_buckets = VL_ULL(1) << new_log2Buckets;
|
||||
Bucket* new_bucketsp = new Bucket[new_num_buckets];
|
||||
|
||||
for (size_t i=0; i<numBuckets(); i++) {
|
||||
while (!m_bucketsp[i].empty()) {
|
||||
typename Bucket::iterator bit = m_bucketsp[i].begin();
|
||||
size_t hash = m_hash.operator()(*bit);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] m_bucketsp;
|
||||
m_bucketsp = new_bucketsp;
|
||||
m_log2Buckets = new_log2Buckets;
|
||||
}
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
//
|
||||
/// Functional clone of the std::unordered_map hash table.
|
||||
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:
|
||||
// TYPES
|
||||
typedef std::pair<T_Key, T_Value> KeyValPair;
|
||||
|
||||
class KeyHash {
|
||||
private:
|
||||
T_Hash key_hash;
|
||||
public:
|
||||
KeyHash() {}
|
||||
size_t operator()(const KeyValPair& kv_pair) const {
|
||||
return key_hash.operator()(kv_pair.first);
|
||||
}
|
||||
};
|
||||
|
||||
class KeyEqual {
|
||||
private:
|
||||
T_Equal key_eq;
|
||||
public:
|
||||
KeyEqual() {}
|
||||
bool operator()(const KeyValPair& kv_a, const KeyValPair& kv_b) const {
|
||||
return key_eq.operator()(kv_a.first, kv_b.first);
|
||||
}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
typedef vl_unordered_set<KeyValPair, KeyHash, KeyEqual> MapSet;
|
||||
MapSet m_set; // Wrap this vl_unordered_set which holds all state.
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
vl_unordered_map() {}
|
||||
~vl_unordered_map() {}
|
||||
|
||||
typedef typename MapSet::iterator iterator;
|
||||
typedef typename MapSet::const_iterator const_iterator;
|
||||
|
||||
// METHODS
|
||||
iterator begin() { return m_set.begin(); }
|
||||
const_iterator begin() const { return m_set.begin(); }
|
||||
const_iterator end() const { return m_set.end(); }
|
||||
bool empty() const { return m_set.empty(); }
|
||||
iterator find(const T_Key& k) {
|
||||
// We can't assume that T_Value() is defined.
|
||||
// ie, this does not work:
|
||||
// return m_set.find(std::make_pair(k, T_Value()));
|
||||
|
||||
// So, do this instead:
|
||||
T_Hash mapHash;
|
||||
T_Equal mapEq;
|
||||
size_t hash = mapHash.operator()(k);
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
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; }
|
||||
m_set.erase(it);
|
||||
return 1;
|
||||
}
|
||||
T_Value& operator[](const T_Key& k) {
|
||||
// Here we can assume T_Value() is defined, as
|
||||
// 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;
|
||||
}
|
||||
// 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:
|
||||
return it->second;
|
||||
}
|
||||
T_Value& at(const T_Key& k) {
|
||||
iterator it = find(k);
|
||||
if (it == end()) { throw std::out_of_range("sorry"); }
|
||||
return it->second;
|
||||
}
|
||||
const T_Value& at(const T_Key& k) const {
|
||||
iterator it = find(k);
|
||||
if (it == end()) { throw std::out_of_range("sorry"); }
|
||||
return it->second;
|
||||
}
|
||||
void clear() { m_set.clear(); }
|
||||
size_t size() const { return m_set.size(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
// SPDIFF_ON
|
||||
|
||||
#ifndef O_LARGEFILE // For example on WIN32
|
||||
#ifndef O_LARGEFILE // For example on WIN32
|
||||
# define O_LARGEFILE 0
|
||||
#endif
|
||||
#ifndef O_NONBLOCK
|
||||
|
|
@ -58,25 +58,25 @@ private:
|
|||
VerilatedMutex s_vcdMutex; ///< Protect the singleton
|
||||
VcdVec s_vcdVecp VL_GUARDED_BY(s_vcdMutex); ///< List of all created traces
|
||||
};
|
||||
static Singleton& singleton() { static Singleton s; return s; }
|
||||
static Singleton& singleton() { static Singleton s; return s; }
|
||||
public:
|
||||
static void pushVcd(VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
singleton().s_vcdVecp.push_back(vcdp);
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
singleton().s_vcdVecp.push_back(vcdp);
|
||||
}
|
||||
static void removeVcd(const VerilatedVcd* vcdp) VL_EXCLUDES(singleton().s_vcdMutex) {
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
VcdVec::iterator pos = find(singleton().s_vcdVecp.begin(), singleton().s_vcdVecp.end(), vcdp);
|
||||
if (pos != singleton().s_vcdVecp.end()) { singleton().s_vcdVecp.erase(pos); }
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
VcdVec::iterator pos = find(singleton().s_vcdVecp.begin(), singleton().s_vcdVecp.end(), vcdp);
|
||||
if (pos != singleton().s_vcdVecp.end()) { singleton().s_vcdVecp.erase(pos); }
|
||||
}
|
||||
static void flush_all() VL_EXCLUDES(singleton().s_vcdMutex) VL_MT_UNSAFE_ONE {
|
||||
// Thread safety: Although this function is protected by a mutex so perhaps
|
||||
// in the future we can allow tracing in separate threads, vcdp->flush() assumes call from single thread
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
for (VcdVec::const_iterator it=singleton().s_vcdVecp.begin(); it!=singleton().s_vcdVecp.end(); ++it) {
|
||||
VerilatedVcd* vcdp = *it;
|
||||
vcdp->flush();
|
||||
}
|
||||
// Thread safety: Although this function is protected by a mutex so perhaps
|
||||
// in the future we can allow tracing in separate threads, vcdp->flush() assumes call from single thread
|
||||
VerilatedLockGuard lock(singleton().s_vcdMutex);
|
||||
for (VcdVec::const_iterator it=singleton().s_vcdVecp.begin(); it!=singleton().s_vcdVecp.end(); ++it) {
|
||||
VerilatedVcd* vcdp = *it;
|
||||
vcdp->flush();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -91,16 +91,16 @@ public:
|
|||
class VerilatedVcdCallInfo {
|
||||
protected:
|
||||
friend class VerilatedVcd;
|
||||
VerilatedVcdCallback_t m_initcb; ///< Initialization Callback function
|
||||
VerilatedVcdCallback_t m_fullcb; ///< Full Dumping Callback function
|
||||
VerilatedVcdCallback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< Fake "this" for caller
|
||||
vluint32_t m_code; ///< Starting code number
|
||||
VerilatedVcdCallback_t m_initcb; ///< Initialization Callback function
|
||||
VerilatedVcdCallback_t m_fullcb; ///< Full Dumping Callback function
|
||||
VerilatedVcdCallback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< Fake "this" for caller
|
||||
vluint32_t m_code; ///< Starting code number
|
||||
// CONSTRUCTORS
|
||||
VerilatedVcdCallInfo (VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
|
||||
VerilatedVcdCallback_t changecb,
|
||||
void* ut, vluint32_t code)
|
||||
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {};
|
||||
VerilatedVcdCallInfo(VerilatedVcdCallback_t icb, VerilatedVcdCallback_t fcb,
|
||||
VerilatedVcdCallback_t changecb,
|
||||
void* ut, vluint32_t code)
|
||||
: m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {};
|
||||
~VerilatedVcdCallInfo() {}
|
||||
};
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep)
|
|||
m_wroteBytes = 0;
|
||||
}
|
||||
|
||||
void VerilatedVcd::open (const char* filename) {
|
||||
void VerilatedVcd::open(const char* filename) {
|
||||
m_assertOne.check();
|
||||
if (isOpen()) return;
|
||||
|
||||
|
|
@ -159,64 +159,64 @@ void VerilatedVcd::open (const char* filename) {
|
|||
Verilated::flushCb(&flush_all);
|
||||
|
||||
// SPDIFF_ON
|
||||
openNext (m_rolloverMB!=0);
|
||||
openNext(m_rolloverMB!=0);
|
||||
if (!isOpen()) return;
|
||||
|
||||
dumpHeader();
|
||||
|
||||
// Allocate space now we know the number of codes
|
||||
if (!m_sigs_oldvalp) {
|
||||
m_sigs_oldvalp = new vluint32_t [m_nextCode+10];
|
||||
m_sigs_oldvalp = new vluint32_t [m_nextCode+10];
|
||||
}
|
||||
|
||||
if (m_rolloverMB) {
|
||||
openNext(true);
|
||||
if (!isOpen()) return;
|
||||
openNext(true);
|
||||
if (!isOpen()) return;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::openNext (bool incFilename) {
|
||||
void VerilatedVcd::openNext(bool incFilename) {
|
||||
// Open next filename in concat sequence, mangle filename if
|
||||
// incFilename is true.
|
||||
m_assertOne.check();
|
||||
closePrev(); // Close existing
|
||||
closePrev(); // Close existing
|
||||
if (incFilename) {
|
||||
// Find _0000.{ext} in filename
|
||||
std::string name = m_filename;
|
||||
size_t pos=name.rfind(".");
|
||||
if (pos>8 && 0==strncmp("_cat",name.c_str()+pos-8,4)
|
||||
&& isdigit(name.c_str()[pos-4])
|
||||
&& isdigit(name.c_str()[pos-3])
|
||||
&& isdigit(name.c_str()[pos-2])
|
||||
&& isdigit(name.c_str()[pos-1])) {
|
||||
// Increment code.
|
||||
if ((++(name[pos-1])) > '9') {
|
||||
name[pos-1] = '0';
|
||||
if ((++(name[pos-2])) > '9') {
|
||||
name[pos-2] = '0';
|
||||
if ((++(name[pos-3])) > '9') {
|
||||
name[pos-3] = '0';
|
||||
if ((++(name[pos-4])) > '9') {
|
||||
name[pos-4] = '0';
|
||||
}}}}
|
||||
} else {
|
||||
// Append _cat0000
|
||||
name.insert(pos,"_cat0000");
|
||||
}
|
||||
m_filename = name;
|
||||
// Find _0000.{ext} in filename
|
||||
std::string name = m_filename;
|
||||
size_t pos=name.rfind(".");
|
||||
if (pos>8 && 0==strncmp("_cat",name.c_str()+pos-8,4)
|
||||
&& isdigit(name.c_str()[pos-4])
|
||||
&& isdigit(name.c_str()[pos-3])
|
||||
&& isdigit(name.c_str()[pos-2])
|
||||
&& isdigit(name.c_str()[pos-1])) {
|
||||
// Increment code.
|
||||
if ((++(name[pos-1])) > '9') {
|
||||
name[pos-1] = '0';
|
||||
if ((++(name[pos-2])) > '9') {
|
||||
name[pos-2] = '0';
|
||||
if ((++(name[pos-3])) > '9') {
|
||||
name[pos-3] = '0';
|
||||
if ((++(name[pos-4])) > '9') {
|
||||
name[pos-4] = '0';
|
||||
}}}}
|
||||
} else {
|
||||
// Append _cat0000
|
||||
name.insert(pos,"_cat0000");
|
||||
}
|
||||
m_filename = name;
|
||||
}
|
||||
if (m_filename[0]=='|') {
|
||||
assert(0); // Not supported yet.
|
||||
assert(0); // Not supported yet.
|
||||
} else {
|
||||
// cppcheck-suppress duplicateExpression
|
||||
if (!m_filep->open(m_filename)) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
return;
|
||||
}
|
||||
// cppcheck-suppress duplicateExpression
|
||||
if (!m_filep->open(m_filename)) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_isOpen = true;
|
||||
m_fullDump = true; // First dump must be full
|
||||
m_fullDump = true; // First dump must be full
|
||||
m_wroteBytes = 0;
|
||||
}
|
||||
|
||||
|
|
@ -226,9 +226,9 @@ void VerilatedVcd::makeNameMap() {
|
|||
m_nextCode = 1;
|
||||
m_namemapp = new NameMap;
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
cip->m_code = m_nextCode;
|
||||
(cip->m_initcb) (this, cip->m_userthis, cip->m_code);
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
cip->m_code = m_nextCode;
|
||||
(cip->m_initcb) (this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
|
||||
// Though not speced, it's illegal to generate a vcd with signals
|
||||
|
|
@ -237,21 +237,21 @@ void VerilatedVcd::makeNameMap() {
|
|||
// This comes from user instantiations with no name - IE Vtop("").
|
||||
bool nullScope = false;
|
||||
for (NameMap::const_iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
|
||||
const std::string& hiername = it->first;
|
||||
if (hiername.size() >= 1 && hiername[0] == '\t') nullScope=true;
|
||||
const std::string& hiername = it->first;
|
||||
if (hiername.size() >= 1 && hiername[0] == '\t') nullScope=true;
|
||||
}
|
||||
if (nullScope) {
|
||||
NameMap* newmapp = new NameMap;
|
||||
for (NameMap::const_iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
|
||||
const std::string& hiername = it->first;
|
||||
const std::string& decl = it->second;
|
||||
std::string newname = std::string("top");
|
||||
if (hiername[0] != '\t') newname += ' ';
|
||||
newname += hiername;
|
||||
newmapp->insert(std::make_pair(newname,decl));
|
||||
}
|
||||
deleteNameMap();
|
||||
m_namemapp = newmapp;
|
||||
NameMap* newmapp = new NameMap;
|
||||
for (NameMap::const_iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
|
||||
const std::string& hiername = it->first;
|
||||
const std::string& decl = it->second;
|
||||
std::string newname = std::string("top");
|
||||
if (hiername[0] != '\t') newname += ' ';
|
||||
newname += hiername;
|
||||
newmapp->insert(std::make_pair(newname,decl));
|
||||
}
|
||||
deleteNameMap();
|
||||
m_namemapp = newmapp;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,13 +266,13 @@ VerilatedVcd::~VerilatedVcd() {
|
|||
deleteNameMap();
|
||||
if (m_filep && m_fileNewed) { delete m_filep; m_filep = NULL; }
|
||||
for (CallbackVec::const_iterator it=m_callbacks.begin(); it!=m_callbacks.end(); ++it) {
|
||||
delete (*it);
|
||||
delete (*it);
|
||||
}
|
||||
m_callbacks.clear();
|
||||
VerilatedVcdSingleton::removeVcd(this);
|
||||
}
|
||||
|
||||
void VerilatedVcd::closePrev () {
|
||||
void VerilatedVcd::closePrev() {
|
||||
// This function is on the flush() call path
|
||||
if (!isOpen()) return;
|
||||
|
||||
|
|
@ -281,7 +281,7 @@ void VerilatedVcd::closePrev () {
|
|||
m_filep->close();
|
||||
}
|
||||
|
||||
void VerilatedVcd::closeErr () {
|
||||
void VerilatedVcd::closeErr() {
|
||||
// This function is on the flush() call path
|
||||
// Close due to an error. We might abort before even getting here,
|
||||
// depending on the definition of vl_fatal.
|
||||
|
|
@ -297,37 +297,37 @@ void VerilatedVcd::close() {
|
|||
m_assertOne.check();
|
||||
if (!isOpen()) return;
|
||||
if (m_evcd) {
|
||||
printStr("$vcdclose ");
|
||||
printTime(m_timeLastDump);
|
||||
printStr(" $end\n");
|
||||
printStr("$vcdclose ");
|
||||
printTime(m_timeLastDump);
|
||||
printStr(" $end\n");
|
||||
}
|
||||
closePrev();
|
||||
}
|
||||
|
||||
void VerilatedVcd::printStr (const char* str) {
|
||||
void VerilatedVcd::printStr(const char* str) {
|
||||
// Not fast...
|
||||
while (*str) {
|
||||
*m_writep++ = *str++;
|
||||
bufferCheck();
|
||||
*m_writep++ = *str++;
|
||||
bufferCheck();
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::printQuad (vluint64_t n) {
|
||||
void VerilatedVcd::printQuad(vluint64_t n) {
|
||||
char buf [100];
|
||||
sprintf(buf,"%" VL_PRI64 "u", n);
|
||||
printStr(buf);
|
||||
}
|
||||
|
||||
void VerilatedVcd::printTime (vluint64_t timeui) {
|
||||
void VerilatedVcd::printTime(vluint64_t timeui) {
|
||||
// VCD file format specification does not allow non-integers for timestamps
|
||||
// Dinotrace doesn't mind, but Cadence vvision seems to choke
|
||||
if (VL_UNLIKELY(timeui < m_timeLastDump)) {
|
||||
timeui = m_timeLastDump;
|
||||
static VL_THREAD_LOCAL bool backTime = false;
|
||||
if (!backTime) {
|
||||
backTime = true;
|
||||
VL_PRINTF_MT("%%Warning: VCD time is moving backwards, wave file may be incorrect.\n");
|
||||
}
|
||||
timeui = m_timeLastDump;
|
||||
static VL_THREAD_LOCAL bool backTime = false;
|
||||
if (!backTime) {
|
||||
backTime = true;
|
||||
VL_PRINTF_MT("%%Warning: VCD time is moving backwards, wave file may be incorrect.\n");
|
||||
}
|
||||
}
|
||||
m_timeLastDump = timeui;
|
||||
printQuad(timeui);
|
||||
|
|
@ -337,17 +337,17 @@ void VerilatedVcd::bufferResize(vluint64_t minsize) {
|
|||
// minsize is size of largest write. We buffer at least 8 times as much data,
|
||||
// writing when we are 3/4 full (with thus 2*minsize remaining free)
|
||||
if (VL_UNLIKELY(minsize > m_wrChunkSize)) {
|
||||
char* oldbufp = m_wrBufp;
|
||||
m_wrChunkSize = minsize*2;
|
||||
m_wrBufp = new char [m_wrChunkSize * 8];
|
||||
memcpy(m_wrBufp, oldbufp, m_writep - oldbufp);
|
||||
char* oldbufp = m_wrBufp;
|
||||
m_wrChunkSize = minsize*2;
|
||||
m_wrBufp = new char [m_wrChunkSize * 8];
|
||||
memcpy(m_wrBufp, oldbufp, m_writep - oldbufp);
|
||||
m_writep = m_wrBufp + (m_writep - oldbufp);
|
||||
m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
|
||||
delete [] oldbufp; oldbufp=NULL;
|
||||
m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
|
||||
delete [] oldbufp; oldbufp=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::bufferFlush () VL_MT_UNSAFE_ONE {
|
||||
void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
// This function is on the flush() call path
|
||||
// We add output data to m_writep.
|
||||
// When it gets nearly full we dump it using this routine which calls write()
|
||||
|
|
@ -356,22 +356,22 @@ void VerilatedVcd::bufferFlush () VL_MT_UNSAFE_ONE {
|
|||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
char* wp = m_wrBufp;
|
||||
while (1) {
|
||||
ssize_t remaining = (m_writep - wp);
|
||||
if (remaining==0) break;
|
||||
errno = 0;
|
||||
ssize_t got = m_filep->write(wp, remaining);
|
||||
if (got>0) {
|
||||
wp += got;
|
||||
m_wroteBytes += got;
|
||||
} else if (got < 0) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string("VerilatedVcd::bufferFlush: ")+strerror(errno);
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
closeErr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
ssize_t remaining = (m_writep - wp);
|
||||
if (remaining==0) break;
|
||||
errno = 0;
|
||||
ssize_t got = m_filep->write(wp, remaining);
|
||||
if (got>0) {
|
||||
wp += got;
|
||||
m_wroteBytes += got;
|
||||
} else if (got < 0) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string("VerilatedVcd::bufferFlush: ")+strerror(errno);
|
||||
VL_FATAL_MT("",0,"",msg.c_str());
|
||||
closeErr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset buffer
|
||||
|
|
@ -381,19 +381,19 @@ void VerilatedVcd::bufferFlush () VL_MT_UNSAFE_ONE {
|
|||
//=============================================================================
|
||||
// Simple methods
|
||||
|
||||
void VerilatedVcd::set_time_unit (const char* unitp) {
|
||||
//cout<<" set_time_unit ("<<unitp<<") == "<<timescaleToDouble(unitp)
|
||||
void VerilatedVcd::set_time_unit(const char* unitp) {
|
||||
//cout<<" set_time_unit("<<unitp<<") == "<<timescaleToDouble(unitp)
|
||||
// <<" == "<<doubleToTimescale(timescaleToDouble(unitp))<<endl;
|
||||
m_timeUnit = timescaleToDouble(unitp);
|
||||
}
|
||||
|
||||
void VerilatedVcd::set_time_resolution (const char* unitp) {
|
||||
//cout<<"set_time_resolution ("<<unitp<<") == "<<timescaleToDouble(unitp)
|
||||
void VerilatedVcd::set_time_resolution(const char* unitp) {
|
||||
//cout<<"set_time_resolution("<<unitp<<") == "<<timescaleToDouble(unitp)
|
||||
// <<" == "<<doubleToTimescale(timescaleToDouble(unitp))<<endl;
|
||||
m_timeRes = timescaleToDouble(unitp);
|
||||
}
|
||||
|
||||
double VerilatedVcd::timescaleToDouble (const char* unitp) {
|
||||
double VerilatedVcd::timescaleToDouble(const char* unitp) {
|
||||
char* endp;
|
||||
double value = strtod(unitp, &endp);
|
||||
if (value==0.0 && endp==unitp) value=1; // On error so we allow just "ns" to return 1e-9.
|
||||
|
|
@ -411,9 +411,9 @@ double VerilatedVcd::timescaleToDouble (const char* unitp) {
|
|||
return value;
|
||||
}
|
||||
|
||||
std::string VerilatedVcd::doubleToTimescale (double value) {
|
||||
std::string VerilatedVcd::doubleToTimescale(double value) {
|
||||
const char* suffixp = "s";
|
||||
if (value>=1e0) { suffixp="s"; value *= 1e0; }
|
||||
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; }
|
||||
|
|
@ -427,14 +427,14 @@ std::string VerilatedVcd::doubleToTimescale (double value) {
|
|||
//=============================================================================
|
||||
// Definitions
|
||||
|
||||
void VerilatedVcd::printIndent (int level_change) {
|
||||
void VerilatedVcd::printIndent(int level_change) {
|
||||
if (level_change<0) m_modDepth += level_change;
|
||||
assert(m_modDepth>=0);
|
||||
for (int i=0; i<m_modDepth; i++) printStr(" ");
|
||||
if (level_change>0) m_modDepth += level_change;
|
||||
}
|
||||
|
||||
void VerilatedVcd::dumpHeader () {
|
||||
void VerilatedVcd::dumpHeader() {
|
||||
printStr("$version Generated by VerilatedVcd $end\n");
|
||||
time_t time_str = time(NULL);
|
||||
printStr("$date "); printStr(ctime(&time_str)); printStr(" $end\n");
|
||||
|
|
@ -447,7 +447,7 @@ void VerilatedVcd::dumpHeader () {
|
|||
makeNameMap();
|
||||
|
||||
// Signal header
|
||||
assert (m_modDepth==0);
|
||||
assert(m_modDepth==0);
|
||||
printIndent(1);
|
||||
printStr("\n");
|
||||
|
||||
|
|
@ -459,78 +459,78 @@ void VerilatedVcd::dumpHeader () {
|
|||
// Print the signal names
|
||||
const char* lastName = "";
|
||||
for (NameMap::const_iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
|
||||
const std::string& hiernamestr = it->first;
|
||||
const std::string& decl = it->second;
|
||||
const std::string& hiernamestr = it->first;
|
||||
const std::string& decl = it->second;
|
||||
|
||||
// Determine difference between the old and new names
|
||||
const char* hiername = hiernamestr.c_str();
|
||||
const char* lp = lastName;
|
||||
const char* np = hiername;
|
||||
lastName = hiername;
|
||||
// Determine difference between the old and new names
|
||||
const char* hiername = hiernamestr.c_str();
|
||||
const char* lp = lastName;
|
||||
const char* np = hiername;
|
||||
lastName = hiername;
|
||||
|
||||
// Skip common prefix, it must break at a space or tab
|
||||
for (; *np && (*np == *lp); np++, lp++) {}
|
||||
while (np!=hiername && *np && *np!=' ' && *np!='\t') { np--; lp--; }
|
||||
//printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np);
|
||||
// Skip common prefix, it must break at a space or tab
|
||||
for (; *np && (*np == *lp); np++, lp++) {}
|
||||
while (np!=hiername && *np && *np!=' ' && *np!='\t') { np--; lp--; }
|
||||
//printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np);
|
||||
|
||||
// Any extra spaces in last name are scope ups we need to do
|
||||
bool first = true;
|
||||
for (; *lp; lp++) {
|
||||
if (*lp==' ' || (first && *lp!='\t')) {
|
||||
printIndent(-1);
|
||||
printStr("$upscope $end\n");
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
// Any extra spaces in last name are scope ups we need to do
|
||||
bool first = true;
|
||||
for (; *lp; lp++) {
|
||||
if (*lp==' ' || (first && *lp!='\t')) {
|
||||
printIndent(-1);
|
||||
printStr("$upscope $end\n");
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
// Any new spaces are scope downs we need to do
|
||||
while (*np) {
|
||||
if (*np==' ') np++;
|
||||
if (*np=='\t') break; // tab means signal name starts
|
||||
printIndent(1);
|
||||
printStr("$scope module ");
|
||||
for (; *np && *np!=' ' && *np!='\t'; np++) {
|
||||
if (*np=='[') printStr("(");
|
||||
else if (*np==']') printStr(")");
|
||||
else *m_writep++=*np;
|
||||
}
|
||||
printStr(" $end\n");
|
||||
}
|
||||
// Any new spaces are scope downs we need to do
|
||||
while (*np) {
|
||||
if (*np==' ') np++;
|
||||
if (*np=='\t') break; // tab means signal name starts
|
||||
printIndent(1);
|
||||
printStr("$scope module ");
|
||||
for (; *np && *np!=' ' && *np!='\t'; np++) {
|
||||
if (*np=='[') printStr("(");
|
||||
else if (*np==']') printStr(")");
|
||||
else *m_writep++=*np;
|
||||
}
|
||||
printStr(" $end\n");
|
||||
}
|
||||
|
||||
printIndent(0);
|
||||
printStr(decl.c_str());
|
||||
printIndent(0);
|
||||
printStr(decl.c_str());
|
||||
}
|
||||
|
||||
while (m_modDepth>1) {
|
||||
printIndent(-1);
|
||||
printStr("$upscope $end\n");
|
||||
printIndent(-1);
|
||||
printStr("$upscope $end\n");
|
||||
}
|
||||
|
||||
printIndent(-1);
|
||||
printStr("$enddefinitions $end\n\n\n");
|
||||
assert (m_modDepth==0);
|
||||
assert(m_modDepth==0);
|
||||
|
||||
// Reclaim storage
|
||||
deleteNameMap();
|
||||
}
|
||||
|
||||
void VerilatedVcd::module (const std::string& name) {
|
||||
void VerilatedVcd::module(const std::string& name) {
|
||||
m_assertOne.check();
|
||||
m_modName = name;
|
||||
}
|
||||
|
||||
void VerilatedVcd::declare (vluint32_t code, const char* name, const char* wirep,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb) {
|
||||
void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb) {
|
||||
if (!code) { VL_FATAL_MT(__FILE__,__LINE__,"","Internal: internal trace problem, code 0 is illegal"); }
|
||||
|
||||
int bits = ((msb>lsb)?(msb-lsb):(lsb-msb))+1;
|
||||
int codesNeeded = 1+int(bits/32);
|
||||
if (tri) codesNeeded *= 2; // Space in change array for __en signals
|
||||
if (tri) codesNeeded *= 2; // Space in change array for __en signals
|
||||
|
||||
// Make sure array is large enough
|
||||
m_nextCode = std::max(m_nextCode, code+codesNeeded);
|
||||
if (m_sigs.capacity() <= m_nextCode) {
|
||||
m_sigs.reserve(m_nextCode*2); // Power-of-2 allocation speeds things up
|
||||
m_sigs.reserve(m_nextCode*2); // Power-of-2 allocation speeds things up
|
||||
}
|
||||
|
||||
// Make sure write buffer is large enough (one character per bit), plus header
|
||||
|
|
@ -551,14 +551,14 @@ void VerilatedVcd::declare (vluint32_t code, const char* name, const char* wirep
|
|||
std::string hiername;
|
||||
std::string basename;
|
||||
for (const char* cp=nameasstr.c_str(); *cp; cp++) {
|
||||
if (isScopeEscape(*cp)) {
|
||||
// Ahh, we've just read a scope, not a basename
|
||||
if (hiername!="") hiername += " ";
|
||||
hiername += basename;
|
||||
basename = "";
|
||||
} else {
|
||||
basename += *cp;
|
||||
}
|
||||
if (isScopeEscape(*cp)) {
|
||||
// Ahh, we've just read a scope, not a basename
|
||||
if (hiername!="") hiername += " ";
|
||||
hiername += basename;
|
||||
basename = "";
|
||||
} else {
|
||||
basename += *cp;
|
||||
}
|
||||
}
|
||||
hiername += "\t"+basename;
|
||||
|
||||
|
|
@ -570,49 +570,49 @@ void VerilatedVcd::declare (vluint32_t code, const char* name, const char* wirep
|
|||
decl += buf;
|
||||
if (m_evcd) {
|
||||
sprintf(buf, "<%u", code);
|
||||
decl += buf;
|
||||
decl += buf;
|
||||
} else {
|
||||
decl += stringCode(code);
|
||||
decl += stringCode(code);
|
||||
}
|
||||
decl += " ";
|
||||
decl += basename;
|
||||
if (arraynum>=0) {
|
||||
sprintf(buf, "(%d)", arraynum);
|
||||
decl += buf;
|
||||
hiername += buf;
|
||||
sprintf(buf, "(%d)", arraynum);
|
||||
decl += buf;
|
||||
hiername += buf;
|
||||
}
|
||||
if (bussed) {
|
||||
sprintf(buf, " [%d:%d]", msb, lsb);
|
||||
decl += buf;
|
||||
sprintf(buf, " [%d:%d]", msb, lsb);
|
||||
decl += buf;
|
||||
}
|
||||
decl += " $end\n";
|
||||
m_namemapp->insert(std::make_pair(hiername,decl));
|
||||
}
|
||||
|
||||
void VerilatedVcd::declBit (vluint32_t code, const char* name, int arraynum)
|
||||
{ declare (code, name, "wire", arraynum, false, false, 0, 0); }
|
||||
{ declare(code, name, "wire", arraynum, false, false, 0, 0); }
|
||||
void VerilatedVcd::declBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
void VerilatedVcd::declQuad (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
void VerilatedVcd::declArray (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, false, true, msb, lsb); }
|
||||
void VerilatedVcd::declTriBit (vluint32_t code, const char* name, int arraynum)
|
||||
{ declare (code, name, "wire", arraynum, true, false, 0, 0); }
|
||||
{ declare(code, name, "wire", arraynum, true, false, 0, 0); }
|
||||
void VerilatedVcd::declTriBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
void VerilatedVcd::declTriQuad (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
void VerilatedVcd::declTriArray (vluint32_t code, const char* name, int arraynum, int msb, int lsb)
|
||||
{ declare (code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
{ declare(code, name, "wire", arraynum, true, true, msb, lsb); }
|
||||
void VerilatedVcd::declFloat (vluint32_t code, const char* name, int arraynum)
|
||||
{ declare (code, name, "real", arraynum, false, false, 31, 0); }
|
||||
{ declare(code, name, "real", arraynum, false, false, 31, 0); }
|
||||
void VerilatedVcd::declDouble (vluint32_t code, const char* name, int arraynum)
|
||||
{ declare (code, name, "real", arraynum, false, false, 63, 0); }
|
||||
{ declare(code, name, "real", arraynum, false, false, 63, 0); }
|
||||
|
||||
//=============================================================================
|
||||
|
||||
void VerilatedVcd::fullDouble (vluint32_t code, const double newval) {
|
||||
void VerilatedVcd::fullDouble(vluint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
(*(reinterpret_cast<double*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
// Buffer can't overflow before sprintf; we sized during declaration
|
||||
|
|
@ -621,7 +621,7 @@ void VerilatedVcd::fullDouble (vluint32_t code, const double newval) {
|
|||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullFloat (vluint32_t code, const float newval) {
|
||||
void VerilatedVcd::fullFloat(vluint32_t code, const float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
(*(reinterpret_cast<float*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
// Buffer can't overflow before sprintf; we sized during declaration
|
||||
|
|
@ -634,14 +634,14 @@ void VerilatedVcd::fullFloat (vluint32_t code, const float newval) {
|
|||
//=============================================================================
|
||||
// Callbacks
|
||||
|
||||
void VerilatedVcd::addCallback VL_MT_UNSAFE_ONE (
|
||||
void VerilatedVcd::addCallback(
|
||||
VerilatedVcdCallback_t initcb, VerilatedVcdCallback_t fullcb, VerilatedVcdCallback_t changecb,
|
||||
void* userthis)
|
||||
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());
|
||||
std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file";
|
||||
VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str());
|
||||
}
|
||||
VerilatedVcdCallInfo* vci = new VerilatedVcdCallInfo(initcb, fullcb, changecb, userthis, m_nextCode);
|
||||
m_callbacks.push_back(vci);
|
||||
|
|
@ -650,37 +650,37 @@ void VerilatedVcd::addCallback VL_MT_UNSAFE_ONE (
|
|||
//=============================================================================
|
||||
// Dumping
|
||||
|
||||
void VerilatedVcd::dumpFull (vluint64_t timeui) {
|
||||
void VerilatedVcd::dumpFull(vluint64_t timeui) {
|
||||
m_assertOne.check();
|
||||
dumpPrep (timeui);
|
||||
dumpPrep(timeui);
|
||||
Verilated::quiesce();
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
(cip->m_fullcb) (this, cip->m_userthis, cip->m_code);
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
(cip->m_fullcb) (this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::dump (vluint64_t timeui) {
|
||||
void VerilatedVcd::dump(vluint64_t timeui) {
|
||||
m_assertOne.check();
|
||||
if (!isOpen()) return;
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No need for more full dumps
|
||||
dumpFull(timeui);
|
||||
return;
|
||||
m_fullDump = false; // No need for more full dumps
|
||||
dumpFull(timeui);
|
||||
return;
|
||||
}
|
||||
if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > this->m_rolloverMB)) {
|
||||
openNext(true);
|
||||
if (!isOpen()) return;
|
||||
openNext(true);
|
||||
if (!isOpen()) return;
|
||||
}
|
||||
dumpPrep (timeui);
|
||||
dumpPrep(timeui);
|
||||
Verilated::quiesce();
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
(cip->m_changecb) (this, cip->m_userthis, cip->m_code);
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
(cip->m_changecb) (this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::dumpPrep (vluint64_t timeui) {
|
||||
void VerilatedVcd::dumpPrep(vluint64_t timeui) {
|
||||
printStr("#");
|
||||
printTime(timeui);
|
||||
printStr("\n");
|
||||
|
|
@ -705,40 +705,40 @@ vluint8_t ch;
|
|||
vluint64_t timestamp = 1;
|
||||
double doub = 0;
|
||||
|
||||
void vcdInit (VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
void vcdInit(VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
vcdp->scopeEscape('.');
|
||||
vcdp->module ("top");
|
||||
vcdp->declBus (0x2, "v1",-1,5,1);
|
||||
vcdp->declBus (0x3, "v2",-1,6,0);
|
||||
vcdp->module ("top.sub1");
|
||||
vcdp->declBit (0x4, "s1",-1);
|
||||
vcdp->declBit (0x5, "ch",-1);
|
||||
vcdp->module ("top.sub2");
|
||||
vcdp->declArray (0x6, "s2",-1, 40,3);
|
||||
vcdp->module("top");
|
||||
vcdp->declBus(0x2, "v1",-1,5,1);
|
||||
vcdp->declBus(0x3, "v2",-1,6,0);
|
||||
vcdp->module("top.sub1");
|
||||
vcdp->declBit(0x4, "s1",-1);
|
||||
vcdp->declBit(0x5, "ch",-1);
|
||||
vcdp->module("top.sub2");
|
||||
vcdp->declArray(0x6, "s2",-1, 40,3);
|
||||
// Note need to add 3 for next code.
|
||||
vcdp->module ("top2");
|
||||
vcdp->declBus (0x2, "t2v1",-1,4,1);
|
||||
vcdp->declTriBit (0x10, "io1", -1);
|
||||
vcdp->declTriBus (0x12, "io5", -1,4,0);
|
||||
vcdp->declTriArray (0x16, "io96",-1,95,0);
|
||||
vcdp->module("top2");
|
||||
vcdp->declBus(0x2, "t2v1",-1,4,1);
|
||||
vcdp->declTriBit (0x10, "io1", -1);
|
||||
vcdp->declTriBus (0x12, "io5", -1,4,0);
|
||||
vcdp->declTriArray(0x16, "io96",-1,95,0);
|
||||
// Note need to add 6 for next code.
|
||||
vcdp->declDouble (0x1c, "doub",-1);
|
||||
vcdp->declDouble (0x1c, "doub",-1);
|
||||
// Note need to add 2 for next code.
|
||||
}
|
||||
|
||||
void vcdFull (VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
void vcdFull(VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
vcdp->fullBus (0x2, v1,5);
|
||||
vcdp->fullBus (0x3, v2,7);
|
||||
vcdp->fullBit (0x4, s1);
|
||||
vcdp->fullBus (0x5, ch,2);
|
||||
vcdp->fullArray(0x6, &s2[0], 38);
|
||||
vcdp->fullTriBit (0x10, tri96[0]&1, tri96__tri[0]&1);
|
||||
vcdp->fullTriBus (0x12, tri96[0]&0x1f, tri96__tri[0]&0x1f, 5);
|
||||
vcdp->fullTriArray (0x16, tri96, tri96__tri, 96);
|
||||
vcdp->fullTriBit (0x10, tri96[0]&1, tri96__tri[0]&1);
|
||||
vcdp->fullTriBus (0x12, tri96[0]&0x1f, tri96__tri[0]&0x1f, 5);
|
||||
vcdp->fullTriArray(0x16, tri96, tri96__tri, 96);
|
||||
vcdp->fullDouble(0x1c, doub);
|
||||
}
|
||||
|
||||
void vcdChange (VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
void vcdChange(VerilatedVcd* vcdp, void* userthis, vluint32_t code) {
|
||||
vcdp->chgBus (0x2, v1,5);
|
||||
vcdp->chgBus (0x3, v2,7);
|
||||
vcdp->chgBit (0x4, s1);
|
||||
|
|
@ -760,34 +760,34 @@ main() {
|
|||
ch = 0;
|
||||
doub = 0;
|
||||
{
|
||||
VerilatedVcdC* vcdp = new VerilatedVcdC;
|
||||
vcdp->spTrace()->addCallback (&vcdInit, &vcdFull, &vcdChange, 0);
|
||||
vcdp->open ("test.vcd");
|
||||
// Dumping
|
||||
vcdp->dump(timestamp++);
|
||||
v1 = 0xfff;
|
||||
tri96[2] = 4; tri96[1] = 2; tri96[0] = 1;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = ~0; // Still tri
|
||||
doub = 1.5;
|
||||
vcdp->dump(timestamp++);
|
||||
v2 = 0x1;
|
||||
s2[1] = 2;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = 0; // enable w/o data change
|
||||
doub = -1.66e13;
|
||||
vcdp->dump(timestamp++);
|
||||
ch = 2;
|
||||
tri96[2] = ~4; tri96[1] = ~2; tri96[0] = ~1;
|
||||
doub = -3.33e-13;
|
||||
vcdp->dump(timestamp++);
|
||||
vcdp->dump(timestamp++);
|
||||
VerilatedVcdC* vcdp = new VerilatedVcdC;
|
||||
vcdp->spTrace()->addCallback(&vcdInit, &vcdFull, &vcdChange, 0);
|
||||
vcdp->open("test.vcd");
|
||||
// Dumping
|
||||
vcdp->dump(timestamp++);
|
||||
v1 = 0xfff;
|
||||
tri96[2] = 4; tri96[1] = 2; tri96[0] = 1;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = ~0; // Still tri
|
||||
doub = 1.5;
|
||||
vcdp->dump(timestamp++);
|
||||
v2 = 0x1;
|
||||
s2[1] = 2;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = 0; // enable w/o data change
|
||||
doub = -1.66e13;
|
||||
vcdp->dump(timestamp++);
|
||||
ch = 2;
|
||||
tri96[2] = ~4; tri96[1] = ~2; tri96[0] = ~1;
|
||||
doub = -3.33e-13;
|
||||
vcdp->dump(timestamp++);
|
||||
vcdp->dump(timestamp++);
|
||||
# ifdef VERILATED_VCD_TEST_64BIT
|
||||
vluint64_t bytesPerDump = 15ULL;
|
||||
for (vluint64_t i=0; i<((1ULL<<32) / bytesPerDump); i++) {
|
||||
v1 = i;
|
||||
vcdp->dump(timestamp++);
|
||||
}
|
||||
vluint64_t bytesPerDump = 15ULL;
|
||||
for (vluint64_t i=0; i<((1ULL<<32) / bytesPerDump); i++) {
|
||||
v1 = i;
|
||||
vcdp->dump(timestamp++);
|
||||
}
|
||||
# endif
|
||||
vcdp->close();
|
||||
vcdp->close();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class VerilatedVcdCallInfo;
|
|||
|
||||
class VerilatedVcdFile {
|
||||
private:
|
||||
int m_fd; ///< File descriptor we're writing to
|
||||
int m_fd; ///< File descriptor we're writing to
|
||||
public:
|
||||
// METHODS
|
||||
VerilatedVcdFile() : m_fd(0) {}
|
||||
|
|
@ -57,10 +57,10 @@ public:
|
|||
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) {}
|
||||
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() {}
|
||||
};
|
||||
|
|
@ -76,75 +76,75 @@ typedef void (*VerilatedVcdCallback_t)(VerilatedVcd* vcdp, void* userthis, vluin
|
|||
|
||||
class 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
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
vluint32_t* m_sigs_oldvalp; ///< Pointer to old signal values
|
||||
vluint32_t* m_sigs_oldvalp; ///< Pointer to old signal values
|
||||
typedef std::vector<VerilatedVcdSig> SigVec;
|
||||
SigVec m_sigs; ///< Pointer to signal information
|
||||
SigVec m_sigs; ///< Pointer to signal information
|
||||
typedef std::vector<VerilatedVcdCallInfo*> CallbackVec;
|
||||
CallbackVec m_callbacks; ///< Routines to perform dumping
|
||||
CallbackVec m_callbacks; ///< Routines to perform dumping
|
||||
typedef std::map<std::string,std::string> NameMap;
|
||||
NameMap* m_namemapp; ///< List of names for the header
|
||||
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() {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
|
||||
bufferFlush();
|
||||
}
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
|
||||
bufferFlush();
|
||||
}
|
||||
}
|
||||
void closePrev();
|
||||
void closeErr();
|
||||
void openNext();
|
||||
void makeNameMap();
|
||||
void deleteNameMap();
|
||||
void printIndent (int levelchange);
|
||||
void printStr (const char* str);
|
||||
void printQuad (vluint64_t n);
|
||||
void printTime (vluint64_t timeui);
|
||||
void declare (vluint32_t code, const char* name, const char* wirep,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb);
|
||||
void printIndent(int levelchange);
|
||||
void printStr(const char* str);
|
||||
void printQuad(vluint64_t n);
|
||||
void printTime(vluint64_t timeui);
|
||||
void declare(vluint32_t code, const char* name, const char* wirep,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb);
|
||||
|
||||
void dumpHeader();
|
||||
void dumpPrep (vluint64_t timeui);
|
||||
void dumpFull (vluint64_t timeui);
|
||||
void dumpPrep(vluint64_t timeui);
|
||||
void dumpFull(vluint64_t timeui);
|
||||
// cppcheck-suppress functionConst
|
||||
void dumpDone ();
|
||||
inline void printCode (vluint32_t code) {
|
||||
if (code>=(94*94*94)) *m_writep++ = static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) *m_writep++ = static_cast<char>((code/94/94)%94+33);
|
||||
if (code>=(94)) *m_writep++ = static_cast<char>((code/94)%94+33);
|
||||
*m_writep++ = static_cast<char>((code)%94+33);
|
||||
void dumpDone();
|
||||
inline void printCode(vluint32_t code) {
|
||||
if (code>=(94*94*94)) *m_writep++ = static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) *m_writep++ = static_cast<char>((code/94/94)%94+33);
|
||||
if (code>=(94)) *m_writep++ = static_cast<char>((code/94)%94+33);
|
||||
*m_writep++ = static_cast<char>((code)%94+33);
|
||||
}
|
||||
static std::string stringCode (vluint32_t code) VL_PURE {
|
||||
std::string out;
|
||||
if (code>=(94*94*94)) out += static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) out += static_cast<char>((code/94/94)%94+33);
|
||||
if (code>=(94)) out += static_cast<char>((code/94)%94+33);
|
||||
return out + static_cast<char>((code)%94+33);
|
||||
static std::string stringCode(vluint32_t code) VL_PURE {
|
||||
std::string out;
|
||||
if (code>=(94*94*94)) out += static_cast<char>((code/94/94/94)%94+33);
|
||||
if (code>=(94*94)) out += static_cast<char>((code/94/94)%94+33);
|
||||
if (code>=(94)) out += static_cast<char>((code/94)%94+33);
|
||||
return out + static_cast<char>((code)%94+33);
|
||||
}
|
||||
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -165,34 +165,34 @@ public:
|
|||
|
||||
// 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 openNext(bool incFilename); ///< Open next data-only file
|
||||
void close() VL_MT_UNSAFE_ONE; ///< Close the file
|
||||
/// 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 set_time_unit (const char* unit); ///< Set time units (s/ms, defaults to ns)
|
||||
void set_time_unit (const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
void set_time_unit(const char* unit); ///< Set time units (s/ms, defaults to ns)
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
|
||||
void set_time_resolution (const char* unit); ///< Set time resolution (s/ms, defaults to ns)
|
||||
void set_time_resolution (const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
void set_time_resolution(const char* unit); ///< Set time resolution (s/ms, defaults to ns)
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
double timescaleToDouble (const char* unitp);
|
||||
std::string doubleToTimescale (double value);
|
||||
double timescaleToDouble(const char* unitp);
|
||||
std::string doubleToTimescale(double value);
|
||||
|
||||
/// Inside dumping routines, called each cycle to make the dump
|
||||
void dump (vluint64_t timeui);
|
||||
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 dumpSeconds(double secs) { dump(static_cast<vluint64_t>(secs * m_timeRes)); }
|
||||
|
||||
/// Inside dumping routines, declare callbacks for tracings
|
||||
void addCallback (VerilatedVcdCallback_t init, VerilatedVcdCallback_t full,
|
||||
VerilatedVcdCallback_t change,
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
void addCallback(VerilatedVcdCallback_t init, VerilatedVcdCallback_t full,
|
||||
VerilatedVcdCallback_t change,
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
|
||||
/// Inside dumping routines, declare a module
|
||||
void module (const std::string& name);
|
||||
void module(const std::string& name);
|
||||
/// Inside dumping routines, declare a signal
|
||||
void declBit (vluint32_t code, const char* name, int arraynum);
|
||||
void declBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
|
|
@ -204,192 +204,192 @@ public:
|
|||
void declTriArray (vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void declDouble (vluint32_t code, const char* name, int arraynum);
|
||||
void declFloat (vluint32_t code, const char* name, int arraynum);
|
||||
// ... other module_start for submodules (based on cell name)
|
||||
// ... other module_start for submodules (based on cell name)
|
||||
|
||||
/// 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 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 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&(1ULL<<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&(1ULL<<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 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 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 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 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)&1ULL)
|
||||
| (((newtri >> bit)&1ULL)<<1ULL)];
|
||||
}
|
||||
*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)&1ULL)
|
||||
| (((newtri >> bit)&1ULL)<<1ULL)];
|
||||
}
|
||||
*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();
|
||||
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();
|
||||
}
|
||||
void fullDouble (vluint32_t code, const double newval);
|
||||
void fullFloat (vluint32_t code, const float newval);
|
||||
void fullDouble(vluint32_t code, const double newval);
|
||||
void fullFloat(vluint32_t code, const float newval);
|
||||
|
||||
/// Inside dumping routines, dump one signal as unknowns
|
||||
/// 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 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 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); }
|
||||
inline void fullQuadX(vluint32_t code, int bits) { fullBusX(code, bits); }
|
||||
inline void fullArrayX(vluint32_t code, int bits) { fullBusX(code, bits); }
|
||||
|
||||
/// Inside dumping routines, dump one signal if it has changed
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgBus (vluint32_t code, const vluint32_t newval, int bits) {
|
||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==32 || (diff & ((1U<<bits)-1) ))) {
|
||||
fullBus (code, newval, bits);
|
||||
}
|
||||
}
|
||||
inline void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==32 || (diff & ((1U<<bits)-1) ))) {
|
||||
fullBus(code, newval, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgQuad (vluint32_t code, const vluint64_t newval, int bits) {
|
||||
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==64 || (diff & ((1ULL<<bits)-1) ))) {
|
||||
fullQuad(code, newval, bits);
|
||||
}
|
||||
}
|
||||
inline void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==64 || (diff & ((1ULL<<bits)-1) ))) {
|
||||
fullQuad(code, newval, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgArray (vluint32_t code, const vluint32_t* newval, 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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
inline void chgArray(vluint32_t code, const vluint32_t* newval, 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);
|
||||
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));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
|
||||
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||
fullTriBit (code, newval, newtri);
|
||||
}
|
||||
}
|
||||
inline void 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));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below is only for back compatibility
|
||||
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||
fullTriBit(code, newval, newtri);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBus (vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
|
||||
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
|
||||
| (m_sigs_oldvalp[code+1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==32 || (diff & ((1U<<bits)-1) ))) {
|
||||
fullTriBus (code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
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));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==32 || (diff & ((1U<<bits)-1) ))) {
|
||||
fullTriBus(code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriQuad (vluint32_t code, const vluint64_t newval, const 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));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==64 || (diff & ((1ULL<<bits)-1) ))) {
|
||||
fullTriQuad(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));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==64 || (diff & ((1ULL<<bits)-1) ))) {
|
||||
fullTriQuad(code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriArray (vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
if (VL_UNLIKELY((m_sigs_oldvalp[code+word*2] ^ newvalp[word])
|
||||
| (m_sigs_oldvalp[code+word*2+1] ^ newtrip[word]))) {
|
||||
fullTriArray (code,newvalp,newtrip,bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgDouble (vluint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<double*>(&m_sigs_oldvalp[code]))) != newval)) {
|
||||
fullDouble (code, newval);
|
||||
}
|
||||
inline void chgDouble(vluint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<double*>(&m_sigs_oldvalp[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)) {
|
||||
fullFloat (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)) {
|
||||
fullFloat(code, newval);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
@ -404,7 +404,7 @@ protected:
|
|||
/// Thread safety: Unless otherwise indicated, every function is VL_MT_UNSAFE_ONE
|
||||
|
||||
class VerilatedVcdC {
|
||||
VerilatedVcd m_sptrace; ///< Trace file being created
|
||||
VerilatedVcd m_sptrace; ///< Trace file being created
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcdC);
|
||||
|
|
@ -431,23 +431,23 @@ public:
|
|||
/// Flush dump
|
||||
void flush() VL_MT_UNSAFE_ONE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
void dump (vluint64_t timeui) { m_sptrace.dump(timeui); }
|
||||
void dump(vluint64_t timeui) { m_sptrace.dump(timeui); }
|
||||
/// Write one cycle of dump data - backward compatible and to reduce
|
||||
/// conversion warnings. It's better to use a vluint64_t time instead.
|
||||
void dump (double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump (vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump (int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
/// Set time units (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
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 char* unit) { m_sptrace.set_time_unit(unit); }
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
/// Set time resolution (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
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 char* unit) { m_sptrace.set_time_resolution(unit); }
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
|
||||
/// Internal class access
|
||||
inline VerilatedVcd* spTrace () { return &m_sptrace; };
|
||||
inline VerilatedVcd* spTrace() { return &m_sptrace; };
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
#endif // guard
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@
|
|||
#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 **) {}
|
||||
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
void VerilatedVcdSc::trace( const tp& object, const std::string& name ) {}
|
||||
|
|
@ -81,8 +81,8 @@ void VerilatedVcdSc::trace (const unsigned int &, const std::string &, const cha
|
|||
#elif (SYSTEMC_VERSION>20011000)
|
||||
// SystemC 2.0.1
|
||||
// cppcheck-suppress unusedFunction
|
||||
void VerilatedVcdSc::write_comment (const sc_string &) {}
|
||||
void VerilatedVcdSc::trace (const unsigned int &, const sc_string &, const char **) {}
|
||||
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 ) {}
|
||||
|
|
@ -124,8 +124,8 @@ void VerilatedVcdSc::trace (const unsigned int &, const sc_string &, const char
|
|||
#else
|
||||
// SystemC 1.2.1beta
|
||||
// cppcheck-suppress unusedFunction
|
||||
void VerilatedVcdSc::write_comment (const sc_string &) {}
|
||||
void VerilatedVcdSc::trace (const unsigned int &, const sc_string &, const char **) {}
|
||||
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 ) {}
|
||||
|
|
|
|||
|
|
@ -43,33 +43,33 @@ class VerilatedVcdSc
|
|||
VL_UNCOPYABLE(VerilatedVcdSc);
|
||||
public:
|
||||
VerilatedVcdSc() {
|
||||
sc_get_curr_simcontext()->add_trace_file(this);
|
||||
sc_get_curr_simcontext()->add_trace_file(this);
|
||||
# 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);
|
||||
spTrace()->set_time_unit(tunits.to_string());
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
// 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);
|
||||
spTrace()->set_time_unit(tunits.to_string());
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
# 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());
|
||||
// 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
|
||||
}
|
||||
virtual ~VerilatedVcdSc() {}
|
||||
|
||||
// METHODS
|
||||
/// Called by SystemC simulate()
|
||||
virtual void cycle (bool delta_cycle) {
|
||||
virtual void cycle(bool delta_cycle) {
|
||||
# if (SYSTEMC_VERSION>20011000)
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
# 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()); }
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
@ -78,10 +78,10 @@ private:
|
|||
|
||||
#ifdef NC_SYSTEMC
|
||||
// Cadence Incisive has these as abstract functions so we must create them
|
||||
virtual void set_time_unit( int exponent10_seconds ) {} // deprecated
|
||||
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
|
||||
#endif
|
||||
#if defined(NC_SYSTEMC) || (SYSTEMC_VERSION>=20111100)
|
||||
virtual void set_time_unit( double v, sc_time_unit tu ) {}
|
||||
virtual void set_time_unit(double v, sc_time_unit tu) {}
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -89,12 +89,12 @@ private:
|
|||
# if (SYSTEMC_VERSION>=20050714)
|
||||
// SystemC 2.1.v1
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
virtual void trace( const tp& object, const std::string& name );
|
||||
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 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 **);
|
||||
virtual void write_comment(const std::string &);
|
||||
virtual void trace(const unsigned int &, const std::string &, const char **);
|
||||
|
||||
#if (SYSTEMC_VERSION>=20171012)
|
||||
DECL_TRACE_METHOD_A( sc_event )
|
||||
|
|
@ -138,14 +138,14 @@ private:
|
|||
# elif (SYSTEMC_VERSION>20011000)
|
||||
// SystemC 2.0.1
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
virtual void trace( const tp& object, const sc_string& name );
|
||||
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 );
|
||||
virtual void trace(const tp& object, const sc_string& name, int width);
|
||||
|
||||
virtual void write_comment (const sc_string &);
|
||||
virtual void trace (const unsigned int &, const sc_string &, const char **);
|
||||
virtual void delta_cycles (bool) {}
|
||||
virtual void space( int n ) {}
|
||||
virtual void write_comment(const sc_string &);
|
||||
virtual void trace(const unsigned int &, const sc_string &, const char **);
|
||||
virtual void delta_cycles(bool) {}
|
||||
virtual void space(int n) {}
|
||||
|
||||
DECL_TRACE_METHOD_A( bool )
|
||||
DECL_TRACE_METHOD_A( sc_bit )
|
||||
|
|
@ -182,12 +182,12 @@ private:
|
|||
# else
|
||||
// SystemC 1.2.1beta
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
virtual void trace( const tp& object, const sc_string& name );
|
||||
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 );
|
||||
virtual void trace(const tp& object, const sc_string& name, int width);
|
||||
|
||||
virtual void write_comment (const sc_string &);
|
||||
virtual void trace (const unsigned int &, const sc_string &, const char **);
|
||||
virtual void write_comment(const sc_string &);
|
||||
virtual void trace(const unsigned int &, const sc_string &, const char **);
|
||||
|
||||
DECL_TRACE_METHOD_A( bool )
|
||||
DECL_TRACE_METHOD_B( unsigned char )
|
||||
|
|
@ -220,4 +220,4 @@ private:
|
|||
# undef DECL_TRACE_METHOD_B
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -157,21 +157,23 @@
|
|||
|
||||
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||
# define VL_EQ_DELETE = delete
|
||||
# define VL_HAS_UNIQUE_PTR
|
||||
# define VL_HAS_UNORDERED_MAP
|
||||
# define VL_HAS_UNORDERED_SET
|
||||
# define vl_unique_ptr std::unique_ptr
|
||||
# define vl_unordered_map std::unordered_map
|
||||
# define vl_unordered_set std::unordered_set
|
||||
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
|
||||
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
|
||||
// By default we use std:: types in C++11.
|
||||
// Define VL_USE_UNORDERED_TYPES to test these pre-C++11 classes
|
||||
# ifdef VL_USE_UNORDERED_TYPES
|
||||
# define VL_INCLUDE_UNORDERED_MAP "verilated_unordered_set_map.h"
|
||||
# define VL_INCLUDE_UNORDERED_SET "verilated_unordered_set_map.h"
|
||||
# else
|
||||
# define vl_unordered_map std::unordered_map
|
||||
# define vl_unordered_set std::unordered_set
|
||||
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
|
||||
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
|
||||
# endif
|
||||
#else
|
||||
# define VL_EQ_DELETE
|
||||
# define vl_unique_ptr std::auto_ptr
|
||||
# define vl_unordered_map std::map
|
||||
# define vl_unordered_set std::set
|
||||
# define VL_INCLUDE_UNORDERED_MAP <map>
|
||||
# define VL_INCLUDE_UNORDERED_SET <set>
|
||||
# define VL_INCLUDE_UNORDERED_MAP "verilated_unordered_set_map.h"
|
||||
# define VL_INCLUDE_UNORDERED_SET "verilated_unordered_set_map.h"
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
|
|
|
|||
246
internals.pod
246
internals.pod
|
|
@ -155,6 +155,221 @@ provided and documented in C<V3GraphAlg.cpp>.
|
|||
|
||||
=back
|
||||
|
||||
=head2 Multithreaded Mode
|
||||
|
||||
In --threads mode, the frontend of the Verilator pipeline is the same as
|
||||
serial mode, up until V3Order.
|
||||
|
||||
V3Order builds a fine-grained, statement-level dependency graph that governs
|
||||
the ordering of code within a single eval() call. In serial mode, that
|
||||
dependency graph is used to order all statements into a total serial order.
|
||||
In parallel mode, the same dependency graph is the starting point for a
|
||||
partitioner (V3Partition).
|
||||
|
||||
The partitioner's goal is to coarsen the fine-grained DAG into a coarser
|
||||
DAG, while maintaining as much available parallelism as possible. Often the
|
||||
partitioner can transform an input graph with millions of nodes into a
|
||||
coarsened execution graph with a few dozen nodes, while maintaining enough
|
||||
parallelism to take advantage of a modern multicore CPU. Runtime
|
||||
synchronization cost is not prohibitive with so few nodes.
|
||||
|
||||
=head3 Partitioning
|
||||
|
||||
Our partitioner is similar to the one Vivek Sarkar described in his 1989
|
||||
paper "Partitioning and Scheduling Parallel Programs for Multiprocessors".
|
||||
|
||||
Let's define some terms:
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<Par Factor>
|
||||
|
||||
The available parallelism or "par-factor" of a DAG is the total cost to
|
||||
execute all nodes, divided by the cost to execute the longest critical path
|
||||
through the graph. This is the speedup you would get from running the graph
|
||||
in parallel, if given infinite CPU cores available and communication and
|
||||
synchronization are zero.
|
||||
|
||||
=item C<Macro Task>
|
||||
|
||||
When the partitioner coarsens the graph, it combines nodes together. Each
|
||||
fine-grained node represents an atomic "task"; combined nodes in the
|
||||
coarsened graph are "macro-tasks". This term comes from Sarkar. Each
|
||||
macro-task executes from start to end on one processor, without any
|
||||
synchronization to any other macro-task during its
|
||||
execution. (Synchronization only happens before the macro-task begins or
|
||||
after it ends.)
|
||||
|
||||
=item C<Edge Contraction>
|
||||
|
||||
Our partitioner, like Sarkar's, primarily relies on "edge contraction" to
|
||||
coarsen the graph. It starts with one macro-task per atomic task and
|
||||
iteratively combines pairs of edge-connected macro-tasks.
|
||||
|
||||
=item C<Local Critical Path>
|
||||
|
||||
Each node in the graph has a "local" critical path. That's the critical
|
||||
path from the start of the graph to the start of the node, plus the node's
|
||||
cost, plus the critical path from the end of the node to the end of the
|
||||
graph.
|
||||
|
||||
=back
|
||||
|
||||
Sarkar calls out an important trade-off: coarsening the graph reduces
|
||||
runtime synchronization overhead among the macro-tasks, but it tends to
|
||||
increase the critical path through the graph and thus reduces par-factor.
|
||||
|
||||
Sarkar's partitioner, and ours, chooses pairs of macro-tasks to merge such
|
||||
that the growth in critical path is minimized. Each candidate merge would
|
||||
result in a new node, which would have some local critical path. We choose
|
||||
the candidate that would produce the shortest local critical path. Repeat
|
||||
until par-factor falls to a target threshold. It's a greedy algorithm, and
|
||||
it's not guaranteed to produce the best partition (which Sarkar proves is
|
||||
NP-hard).
|
||||
|
||||
=head3 Estimating Logic Costs
|
||||
|
||||
To compute the cost of any given path through the graph, Verilator
|
||||
estimates an execution cost for each task. Each macro-task has an execution
|
||||
cost which is simply the sum of its tasks' costs. We assume that
|
||||
communication overhead and synchronization overhead are zero, so the cost
|
||||
of any given path through the graph is simply the sum of macro-task
|
||||
execution costs. Sarkar does almost the same thing, except that he has
|
||||
nonzero estimates for synchronization costs.
|
||||
|
||||
Verilator's cost estimates are assigned by the InstrCountCostVisitor. This
|
||||
class is perhaps the most fragile piece of the multithread implementation.
|
||||
It's easy to have a bug where you count something cheap (eg. accessing one
|
||||
element of a huge array) as if it were expensive (eg. by counting it as if
|
||||
it were an access to the entire array.) Even without such gross bugs, the
|
||||
estimates this produce are only loosely predictive of actual runtime cost.
|
||||
Multithread performance would be better with better runtime costs
|
||||
estimates. This is an area to improve.
|
||||
|
||||
=head3 Scheduling Macro-Tasks at Runtime
|
||||
|
||||
After coarsening the graph, we must schedule the macro-tasks for runtime.
|
||||
Sarkar describes two options: you can dynamically schedule tasks at
|
||||
runtime, with a runtime graph follower. Sarkar calls this the
|
||||
"macro-dataflow model." Verilator does not support this; early experiments
|
||||
with this approach had poor performance.
|
||||
|
||||
The other option is to statically assign macro-tasks to threads, with each
|
||||
thread running its macro-tasks in a static order. Sarkar describes this in
|
||||
Chapter 5. Verilator takes this static approach. The only dynamic aspect is
|
||||
that each macro task may block before starting, to wait until its
|
||||
prerequisites on other threads have finished.
|
||||
|
||||
The synchronization cost is cheap if the prereqs are done. If they're not,
|
||||
fragmentation (idle CPU cores waiting) is possible. This is the major
|
||||
source of overhead in this approach. The --prof-threads switch and the
|
||||
C<verilator_gantt> script can visualize the time lost to such
|
||||
fragmentation.
|
||||
|
||||
=head3 Locating Variables for Best Spatial Locality
|
||||
|
||||
After scheduling all code, we attempt to locate variables in memory such
|
||||
that variables accessed by a single macro-task are close together in
|
||||
memory. This provides "spatial locality" -- when we pull in a 64-byte
|
||||
cache line to access a 2-byte variable, we want the other 62 bytes to be
|
||||
ones we'll also likely access soon, for best cache performance.
|
||||
|
||||
This turns out to be critical for performance. It should allow Verilator
|
||||
to scale to very large models. We don't rely on our working set fitting
|
||||
in any CPU cache; instead we essentially "stream" data into caches from
|
||||
memory. It's not literally streaming, where the address increases
|
||||
monotonically, but it should have similar performance characteristics,
|
||||
so long as each macro-task's dataset fits in one core's local caches.
|
||||
|
||||
To achieve spatial locality, we tag each variable with the set of
|
||||
macro-tasks that access it. Let's call this set the "footprint" of that
|
||||
variable. The variables in a given module have a set of footprints. We can
|
||||
order those footprints to minimize the distance between them (distance is
|
||||
the number of macro-tasks that are different across any two footprints) and
|
||||
then emit all variables into the struct in ordered-footprint order.
|
||||
|
||||
The footprint ordering is literally the traveling salesman problem, and we
|
||||
use a TSP-approximation algorithm to get close to an optimal sort.
|
||||
|
||||
This is an old idea. Simulators designed at DEC in the early 1990s used
|
||||
similar techniques to optimize both single-thread and multi-thread modes.
|
||||
(Verilator does not optimize variable placement for spatial locality in
|
||||
serial mode; that is a possible area for improvement.)
|
||||
|
||||
=head3 Improving Multithreaded Performance Further (a TODO list)
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<Wave Scheduling>
|
||||
|
||||
To allow the verilated model to run in parallel with the testbench, it
|
||||
might be nice to support "wave" scheduling, in which work on a cycle begins
|
||||
before eval() is called or continues after eval() returns. For now all
|
||||
work on a cycle happens during the eval() call, leaving Verilator's threads
|
||||
idle while the testbench (everything outside eval()) is working. This would
|
||||
involve fundamental changes within the partitioner, however, it's probably
|
||||
the best bet for hiding testbench latency.
|
||||
|
||||
=item C<Efficient Dynamic Scheduling>
|
||||
|
||||
To scale to more than a few threads, we may revisit a fully dynamic
|
||||
scheduler. For large (>16 core) systems it might make sense to dedicate an
|
||||
entire core to scheduling, so that scheduler data structures would fit in
|
||||
its L1 cache and thus the cost of traversing priority-ordered ready lists
|
||||
would not be prohibitive.
|
||||
|
||||
=item C<Static Scheduling with Runtime Repack>
|
||||
|
||||
We could modify the static scheduling approach by gathering actual
|
||||
macro-task execution times at run time, and dynamically re-packing the
|
||||
macro-tasks into the threads also at run time. Say, re-pack once every
|
||||
10,000 cycles or something. This has the potential to do better than our
|
||||
static estimates about macro-task run times. It could potentially react to
|
||||
CPU cores that aren't performing equally, due to NUMA or thermal throttling
|
||||
or nonuniform competing memory traffic or whatever.
|
||||
|
||||
=item C<Clock Domain Balancing>
|
||||
|
||||
Right now Verilator makes no attempt to balance clock domains across
|
||||
macro-tasks. For a multi-domain model, that could lead to bad gantt chart
|
||||
fragmentation. This could be improved if it's a real problem in practice.
|
||||
|
||||
=item C<Other Forms of MTask Balancing>
|
||||
|
||||
The largest source of runtime overhead is idle CPUs, which happens due to
|
||||
variance between our predicted runtime for each MTask and its actual
|
||||
runtime. That variance is magnified if MTasks are homogeneous, containing
|
||||
similar repeating logic which was generally close together in source code
|
||||
and which is still packed together even after going through Verilator's
|
||||
digestive tract.
|
||||
|
||||
If Verilator could avoid doing that, and instead would take source logic
|
||||
that was close together and distribute it across MTasks, that would
|
||||
increase the diversity of any given MTask, and this should reduce variance
|
||||
in the cost estimates.
|
||||
|
||||
One way to do that might be to make various "tie breaker" comparison
|
||||
routines in the sources to rely more heavily on randomness, and generally
|
||||
try harder not to keep input nodes together when we have the option to
|
||||
scramble things.
|
||||
|
||||
=item C<Performance Regression>
|
||||
|
||||
It would be nice if we had a regression of large designs, with some
|
||||
diversity of design styles, to test on both single- and multi-threaded
|
||||
modes. This would help to avoid performance regressions, and also to
|
||||
evaluate the optimizations while minimizing the impact of parasitic noise.
|
||||
|
||||
=item C<Per-Instance Classes>
|
||||
|
||||
If we have multiple instances of the same module, and they partition
|
||||
differently (likely; we make no attempt to partition them the same) then
|
||||
the variable sort will be suboptimal for either instance. A possible
|
||||
improvement would be to emit a unique class for each instance of a module,
|
||||
and sort its variables optimally for that instance's code stream.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Verilated Flow
|
||||
|
||||
The evaluation loop outputted by Verilator is designed to allow a single
|
||||
|
|
@ -268,8 +483,8 @@ technique lifted from graph traversal packages).
|
|||
A visitor first clears the one it wants to use by calling
|
||||
C<AstNode::user#ClearTree()>, then it can mark any node's user() with whatever
|
||||
data it wants. Readers just call C<< nodep->user() >>, but may need to cast
|
||||
appropriately, so you'll often see C<< nodep->userp()->castSOMETYPE() >>. At
|
||||
the top of each visitor are comments describing how the C<user()> stuff
|
||||
appropriately, so you'll often see C<< VN_CAST(nodep->userp(), SOMETYPE) >>.
|
||||
At the top of each visitor are comments describing how the C<user()> stuff
|
||||
applies to that visitor class. For example:
|
||||
|
||||
// NODE STATE
|
||||
|
|
@ -299,16 +514,13 @@ as it slows the program down to have to pass vup everywhere.)
|
|||
|
||||
=head2 Iterators
|
||||
|
||||
C<AstNode> provides a set of iterators to facilitate walking over the
|
||||
tree. Each takes two arguments, a visitor, C<v>, of type C<AstNVisitor> and
|
||||
an optional pointer user data, C<vup>, of type C<AstNUser*>. The second is
|
||||
one of the ways to pass parameters to visitors described in L</Visitor
|
||||
Functions>, but its use is now deprecated and should I<not> be used for new
|
||||
visitor classes.
|
||||
C<AstNVisitor> provides a set of iterators to facilitate walking over the
|
||||
tree. Each operates on the current C<AstNVisitor> class (as this) and takes
|
||||
an argument type C<AstNode*>.
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<iterate()>
|
||||
=item C<iterate>
|
||||
|
||||
This just applies the C<accept> method of the C<AstNode> to the visitor
|
||||
function.
|
||||
|
|
@ -349,9 +561,9 @@ to be aware that calling iteration may cause the children to change. For
|
|||
example:
|
||||
|
||||
// nodep->lhsp() is 0x1234000
|
||||
nodep->lhsp()->iterateAndNext(...); // and under covers nodep->lhsp() changes
|
||||
iterateAndNextNull(nodep->lhsp()); // and under covers nodep->lhsp() changes
|
||||
// nodep->lhsp() is 0x5678400
|
||||
nodep->lhsp()->iterateAndNext(...);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
|
||||
Will work fine, as even if the first iterate causes a new node to take the
|
||||
place of the lhsp(), that edit will update nodep->lhsp() and the second
|
||||
|
|
@ -359,9 +571,9 @@ call will correctly see the change. Alternatively:
|
|||
|
||||
lp = nodep->lhsp();
|
||||
// nodep->lhsp() is 0x1234000, lp is 0x1234000
|
||||
lp->iterateAndNext(...); **lhsp=NULL;** // and under covers nodep->lhsp() changes
|
||||
iterateAndNextNull(lp); **lhsp=NULL;** // and under covers nodep->lhsp() changes
|
||||
// nodep->lhsp() is 0x5678400, lp is 0x1234000
|
||||
lp->iterateAndNext(...);
|
||||
iterateAndNextNull(lp);
|
||||
|
||||
This will cause bugs or a core dump, as lp is a dangling pointer. Thus it
|
||||
is advisable to set lhsp=NULL shown in the *'s above to make sure these
|
||||
|
|
@ -370,7 +582,7 @@ V3Width is to use acceptSubtreeReturnEdits, which operates on a single node
|
|||
and returns the new pointer if any. Note acceptSubtreeReturnEdits does not
|
||||
follow nextp() links.
|
||||
|
||||
lp = lp->acceptSubtreeReturnEdits()
|
||||
lp = acceptSubtreeReturnEdits(lp)
|
||||
|
||||
=head2 Identifying derived classes
|
||||
|
||||
|
|
@ -379,16 +591,16 @@ dealing with. For example a visitor might not implement separate C<visit>
|
|||
methods for C<AstIf> and C<AstGenIf>, but just a single method for the base
|
||||
class:
|
||||
|
||||
void visit (AstNodeIf* nodep, AstNUser* vup)
|
||||
void visit (AstNodeIf* nodep)
|
||||
|
||||
However that method might want to specify additional code if it is called
|
||||
for C<AstGenIf>. Verilator does this by providing a C<castSOMETYPE()>
|
||||
for C<AstGenIf>. Verilator does this by providing a C<VN_CAST>
|
||||
method for each possible node type, using C++ C<dynamic_cast>. This either
|
||||
returns a pointer to the object cast to that type (if it is of class
|
||||
C<SOMETYPE>, or a derived class of C<SOMETYPE>) or else NULL. So our
|
||||
C<visit> method could use:
|
||||
|
||||
if (nodep->castAstGenIf()) {
|
||||
if (VN_CAST(nodep, AstGenIf) {
|
||||
<code specific to AstGenIf>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ sub test {
|
|||
-r "nodist/install_test" or die "%Error: Run from the top of the verilator kit,";
|
||||
|
||||
cleanenv();
|
||||
$ENV{VERILATOR_NO_OPT_BUILD} = 1; # Don't build optimized executables; just slows this down
|
||||
run("make distclean") if -r "Makefile";
|
||||
|
||||
# Try building from a scratch area
|
||||
|
|
@ -59,11 +58,12 @@ sub test {
|
|||
run("/bin/mkdir -p $prefix");
|
||||
run("cd $blddir && make install");
|
||||
run("test -e $prefix/share/man/man1/verilator.1");
|
||||
run("test -e $prefix/share/verilator/examples/test_c/Makefile");
|
||||
run("test -e $prefix/share/verilator/examples/tracing_c/Makefile");
|
||||
run("test -e $prefix/share/verilator/include/verilated.h");
|
||||
run("test -e $prefix/bin/verilator");
|
||||
run("test -e $prefix/bin/verilator_bin");
|
||||
run("test -e $prefix/bin/verilator_bin_dbg");
|
||||
run("test -e $prefix/bin/verilator_gantt");
|
||||
run("test -e $prefix/bin/verilator_profcfunc");
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +92,10 @@ sub test {
|
|||
run("cd $dir/obj_dir && make -f Vfoo.mk");
|
||||
run("cd $dir/obj_dir && ./Vfoo");
|
||||
}
|
||||
|
||||
if ($Opt_Stage <= 9) {
|
||||
print "*-* All Finished *-*\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub write_verilog {
|
||||
|
|
@ -116,7 +120,8 @@ sub write_verilog {
|
|||
sub cleanenv {
|
||||
foreach my $var (keys %ENV) {
|
||||
if ($var eq "VERILATOR_ROOT"
|
||||
|| $var eq "VERILATOR_INCLUDE") {
|
||||
|| $var eq "VERILATOR_INCLUDE"
|
||||
|| $var eq "VERILATOR_NO_OPT_BUILD") {
|
||||
print "unset $var # Was '$ENV{$var}'\n";
|
||||
delete $ENV{$var}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,10 @@
|
|||
#### Start of system configuration section. ####
|
||||
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
PERL = @PERL@
|
||||
EXEEXT = @EXEEXT@
|
||||
# VPATH only does sources; fix install_test's not creating ../bin
|
||||
vpath %.h @srcdir@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
|
|
@ -42,9 +43,11 @@ UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
|
|||
#*********************************************************************
|
||||
|
||||
obj_opt:
|
||||
mkdir $@
|
||||
mkdir -p $@
|
||||
obj_dbg:
|
||||
mkdir $@
|
||||
mkdir -p $@
|
||||
../bin:
|
||||
mkdir -p $@
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
|
|
@ -52,23 +55,23 @@ obj_dbg:
|
|||
|
||||
opt: ../bin/verilator_bin
|
||||
ifeq ($(VERILATOR_NO_OPT_BUILD),1) # Faster laptop development... One build
|
||||
../bin/verilator_bin: ../bin/verilator_bin_dbg
|
||||
../bin/verilator_bin: ../bin ../bin/verilator_bin_dbg
|
||||
-rm -rf $@ $@.exe
|
||||
-cp -p $<$(EXEEXT) $@$(EXEEXT)
|
||||
else
|
||||
../bin/verilator_bin: obj_opt prefiles
|
||||
cd obj_opt && $(MAKE) -j 1 TGT=../$@ -f ../Makefile_obj serial
|
||||
cd obj_opt && $(MAKE) TGT=../$@ -f ../Makefile_obj
|
||||
../bin/verilator_bin: obj_opt ../bin prefiles
|
||||
$(MAKE) -C obj_opt -j 1 TGT=../$@ -f ../Makefile_obj serial
|
||||
$(MAKE) -C obj_opt TGT=../$@ -f ../Makefile_obj
|
||||
endif
|
||||
|
||||
dbg: ../bin/verilator_bin_dbg ../bin/verilator_coverage_bin_dbg
|
||||
../bin/verilator_bin_dbg: obj_dbg prefiles
|
||||
cd obj_dbg && $(MAKE) -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
../bin/verilator_bin_dbg: obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
|
||||
../bin/verilator_coverage_bin_dbg: obj_dbg prefiles
|
||||
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
|
||||
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj
|
||||
../bin/verilator_coverage_bin_dbg: obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj
|
||||
|
||||
prefiles::
|
||||
prefiles:: config_rev.h
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ ifeq ($(VL_DEBUG),)
|
|||
COPT = -O2
|
||||
else
|
||||
# Debug
|
||||
COPT = -ggdb -DVL_DEBUG
|
||||
COPT = -ggdb -DVL_DEBUG -D_GLIBCXX_DEBUG
|
||||
# Debug & Profile:
|
||||
#LDFLAGS += -pg -g
|
||||
#COPT = -ggdb -pg -g
|
||||
|
|
@ -93,7 +93,7 @@ endif
|
|||
LIBS = -lm -lstdc++
|
||||
|
||||
CPPFLAGS += -MMD
|
||||
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir)
|
||||
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
|
||||
CPPFLAGS += -DYYDEBUG # Required to get nice error messages
|
||||
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
|
||||
CPPFLAGS += $(COPT)
|
||||
|
|
@ -195,10 +195,12 @@ RAW_OBJS = \
|
|||
V3GraphAlg.o \
|
||||
V3GraphAcyc.o \
|
||||
V3GraphDfa.o \
|
||||
V3GraphPathChecker.o \
|
||||
V3GraphTest.o \
|
||||
V3Hashed.o \
|
||||
V3Inline.o \
|
||||
V3Inst.o \
|
||||
V3InstrCount.o \
|
||||
V3Life.o \
|
||||
V3LifePost.o \
|
||||
V3LinkCells.o \
|
||||
|
|
@ -215,9 +217,12 @@ RAW_OBJS = \
|
|||
V3Order.o \
|
||||
V3Os.o \
|
||||
V3Param.o \
|
||||
V3Partition.o \
|
||||
V3PreShell.o \
|
||||
V3Premit.o \
|
||||
V3Reloop.o \
|
||||
V3Scope.o \
|
||||
V3Scoreboard.o \
|
||||
V3Slice.o \
|
||||
V3Split.o \
|
||||
V3SplitAs.o \
|
||||
|
|
@ -230,6 +235,7 @@ RAW_OBJS = \
|
|||
V3Trace.o \
|
||||
V3TraceDecl.o \
|
||||
V3Tristate.o \
|
||||
V3TSP.o \
|
||||
V3Undriven.o \
|
||||
V3Unknown.o \
|
||||
V3Unroll.o \
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@
|
|||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3SenTree.h" // for SenTreeSet
|
||||
#include VL_INCLUDE_UNORDERED_MAP
|
||||
|
||||
//***** See below for main transformation engine
|
||||
|
||||
|
|
@ -52,11 +54,7 @@
|
|||
|
||||
class ActiveBaseVisitor : public AstNVisitor {
|
||||
protected:
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
class ActiveNamer : public ActiveBaseVisitor {
|
||||
|
|
@ -66,7 +64,11 @@ private:
|
|||
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
|
||||
vector<AstActive*> m_activeVec; // List of sensitive actives, for folding
|
||||
|
||||
SenTreeSet m_activeSens; // Sen lists for each active we've made
|
||||
typedef vl_unordered_map<AstSenTree*, AstActive*> ActiveMap;
|
||||
ActiveMap m_activeMap; // Map sentree to active, for folding.
|
||||
|
||||
// METHODS
|
||||
void addActive(AstActive* nodep) {
|
||||
if (!m_scopep) nodep->v3fatalSrc("NULL scope");
|
||||
|
|
@ -77,8 +79,9 @@ private:
|
|||
m_scopep = nodep;
|
||||
m_iActivep = NULL;
|
||||
m_cActivep = NULL;
|
||||
m_activeVec.clear();
|
||||
nodep->iterateChildren(*this);
|
||||
m_activeSens.clear();
|
||||
m_activeMap.clear();
|
||||
iterateChildren(nodep);
|
||||
// Don't clear scopep, the namer persists beyond this visit
|
||||
}
|
||||
virtual void visit(AstSenTree* nodep) {
|
||||
|
|
@ -91,7 +94,7 @@ private:
|
|||
// Default
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
// METHODS
|
||||
public:
|
||||
|
|
@ -116,22 +119,15 @@ public:
|
|||
}
|
||||
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
|
||||
// Return a sentree in this scope that matches given sense list.
|
||||
// Not the fastest, but scopes tend to have few clocks
|
||||
|
||||
AstActive* activep = NULL;
|
||||
//sitemsp->dumpTree(cout," Lookingfor: ");
|
||||
for (vector<AstActive*>::iterator it = m_activeVec.begin(); it!=m_activeVec.end(); ++it) {
|
||||
activep = *it;
|
||||
if (activep) { // Not deleted
|
||||
// Compare the list
|
||||
AstSenTree* asenp = activep->sensesp();
|
||||
if (asenp->sameTree(sensesp)) {
|
||||
UINFO(8," Found ACTIVE "<<activep<<endl);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
activep = NULL;
|
||||
}
|
||||
found:
|
||||
AstSenTree* activeSenp = m_activeSens.find(sensesp);
|
||||
if (activeSenp) {
|
||||
ActiveMap::iterator it = m_activeMap.find(activeSenp);
|
||||
UASSERT(it != m_activeMap.end(), "Corrupt active map");
|
||||
activep = it->second;
|
||||
}
|
||||
|
||||
// Not found, form a new one
|
||||
if (!activep) {
|
||||
AstSenTree* newsenp = sensesp->cloneTree(false);
|
||||
|
|
@ -140,7 +136,8 @@ public:
|
|||
UINFO(8," New ACTIVE "<<activep<<endl);
|
||||
// Form the sensitivity list
|
||||
addActive(activep);
|
||||
m_activeVec.push_back(activep);
|
||||
m_activeMap[newsenp] = activep;
|
||||
m_activeSens.add(newsenp);
|
||||
// Note actives may have also been added above in the Active visitor
|
||||
}
|
||||
return activep;
|
||||
|
|
@ -154,7 +151,7 @@ public:
|
|||
}
|
||||
virtual ~ActiveNamer() {}
|
||||
void main(AstScope* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -193,7 +190,7 @@ private:
|
|||
if (m_check == CT_SEQ) {
|
||||
AstNode* las = m_assignp;
|
||||
m_assignp = nodep;
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
m_assignp = las;
|
||||
}
|
||||
}
|
||||
|
|
@ -217,7 +214,7 @@ private:
|
|||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
|
|
@ -225,7 +222,7 @@ public:
|
|||
m_alwaysp = nodep;
|
||||
m_check = check;
|
||||
m_assignp = NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ActiveDlyVisitor() {}
|
||||
};
|
||||
|
|
@ -252,7 +249,7 @@ private:
|
|||
// Clear last scope's names, and collect this scope's existing names
|
||||
m_namer.main(nodep);
|
||||
m_scopeFinalp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
// Actives are being formed, so we can ignore any already made
|
||||
|
|
@ -314,8 +311,8 @@ private:
|
|||
// Move always to appropriate ACTIVE based on its sense list
|
||||
if (oldsensesp
|
||||
&& oldsensesp->sensesp()
|
||||
&& oldsensesp->sensesp()->castSenItem()
|
||||
&& oldsensesp->sensesp()->castSenItem()->isNever()) {
|
||||
&& VN_IS(oldsensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
|
|
@ -325,7 +322,7 @@ private:
|
|||
// Read sensitivitues
|
||||
m_itemCombo = false;
|
||||
m_itemSequent = false;
|
||||
if (oldsensesp) oldsensesp->iterateAndNext(*this);
|
||||
iterateAndNextNull(oldsensesp);
|
||||
bool combo = m_itemCombo;
|
||||
bool sequent = m_itemSequent;
|
||||
|
||||
|
|
@ -399,7 +396,7 @@ private:
|
|||
&& subitemp->edgeType() != AstEdgeType::ET_NEGEDGE) {
|
||||
nodep->v3fatalSrc("Strange activity type under SenGate");
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) {
|
||||
if (nodep->edgeType() == AstEdgeType::ET_ANYEDGE) {
|
||||
|
|
@ -421,7 +418,7 @@ private:
|
|||
virtual void visit(AstVarScope* nodep) {}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
|
|
@ -429,7 +426,7 @@ public:
|
|||
m_scopeFinalp = NULL;
|
||||
m_itemCombo = false;
|
||||
m_itemSequent = false;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ActiveVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -58,24 +58,20 @@ private:
|
|||
SenTreeFinder m_finder; // Find global sentree's and add them
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
m_topscopep = nodep;
|
||||
m_finder.main(m_topscopep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_topscopep = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
// Create required actives and add to module
|
||||
// We can start ordering at a module, or a scope
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
|
|
@ -83,8 +79,8 @@ private:
|
|||
AstSenTree* sensesp = nodep->sensesp();
|
||||
if (!sensesp) nodep->v3fatalSrc("NULL");
|
||||
if (sensesp->sensesp()
|
||||
&& sensesp->sensesp()->castSenItem()
|
||||
&& sensesp->sensesp()->castSenItem()->isNever()) {
|
||||
&& VN_IS(sensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(sensesp->sensesp(), SenItem)->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
if (sensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
|
|
@ -118,7 +114,7 @@ private:
|
|||
nodep->sensesp(wantp);
|
||||
}
|
||||
// No need to do statements under it, they're already moved.
|
||||
//nodep->iterateChildren(*this);
|
||||
//iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) {
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
|
|
@ -143,13 +139,13 @@ private:
|
|||
virtual void visit(AstVarScope* nodep) {}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit ActiveTopVisitor(AstNetlist* nodep) {
|
||||
m_topscopep = NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ActiveTopVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -112,13 +112,13 @@ private:
|
|||
AstNode* bodysp = NULL;
|
||||
bool selfDestruct = false;
|
||||
AstIf* ifp = NULL;
|
||||
if (AstPslCover* snodep = nodep->castPslCover()) {
|
||||
if (AstPslCover* snodep = VN_CAST(nodep, PslCover)) {
|
||||
++m_statAsCover;
|
||||
if (!v3Global.opt.coverageUser()) {
|
||||
selfDestruct = true;
|
||||
} else {
|
||||
// V3Coverage assigned us a bucket to increment.
|
||||
AstCoverInc* covincp = snodep->coverincp()->castCoverInc();
|
||||
AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc);
|
||||
if (!covincp) snodep->v3fatalSrc("Missing AstCoverInc under assertion");
|
||||
covincp->unlinkFrBack();
|
||||
if (message!="") covincp->declp()->comment(message);
|
||||
|
|
@ -129,7 +129,7 @@ private:
|
|||
ifp = new AstIf (nodep->fileline(), propp, bodysp, NULL);
|
||||
bodysp = ifp;
|
||||
|
||||
} else if (nodep->castPslAssert()) {
|
||||
} else if (VN_IS(nodep, PslAssert)) {
|
||||
++m_statAsPsl;
|
||||
// Insert an automatic error message and $stop after
|
||||
// any user-supplied statements.
|
||||
|
|
@ -170,7 +170,7 @@ private:
|
|||
AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext();
|
||||
AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext();
|
||||
//
|
||||
if (nodep->castVAssert()) {
|
||||
if (VN_IS(nodep, VAssert)) {
|
||||
if (passsp) passsp = newIfAssertOn(passsp);
|
||||
if (failsp) failsp = newIfAssertOn(failsp);
|
||||
} else {
|
||||
|
|
@ -179,7 +179,7 @@ private:
|
|||
|
||||
AstIf* ifp = new AstIf (nodep->fileline(), propp, passsp, failsp);
|
||||
AstNode* newp = ifp;
|
||||
if (nodep->castVAssert()) ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
if (VN_IS(nodep, VAssert)) ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
//
|
||||
// Install it
|
||||
nodep->replaceWith(newp);
|
||||
|
|
@ -197,14 +197,14 @@ private:
|
|||
// If this statement ends with 'else if', then nextIf will point to the
|
||||
// nextIf statement. Otherwise it will be null.
|
||||
AstNodeIf* nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
|
||||
ifp->condp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(ifp->condp());
|
||||
|
||||
// Recurse into the true case.
|
||||
ifp->ifsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(ifp->ifsp());
|
||||
|
||||
// If the last else is not an else if, recurse into that too.
|
||||
if (ifp->elsesp() && !nextifp) {
|
||||
ifp->elsesp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(ifp->elsesp());
|
||||
}
|
||||
|
||||
// Build a bitmask of the true predicates
|
||||
|
|
@ -242,16 +242,16 @@ private:
|
|||
nodep->replaceWith(checkifp);
|
||||
pushDeletep(nodep);
|
||||
} else {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS //========== Case assertions
|
||||
virtual void visit(AstCase* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
bool has_default=false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) has_default=true;
|
||||
}
|
||||
if (nodep->fullPragma() || nodep->priorityPragma()) {
|
||||
|
|
@ -270,7 +270,7 @@ private:
|
|||
// Not parallel, but harmlessly so.
|
||||
} else {
|
||||
AstNode* propp = NULL;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
AstNode* onep;
|
||||
if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
|
||||
|
|
@ -306,7 +306,7 @@ private:
|
|||
|
||||
// VISITORS //========== Statements
|
||||
virtual void visit(AstDisplay* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Replace the special types with standard text
|
||||
if (nodep->displayType()==AstDisplayType::DT_INFO) {
|
||||
replaceDisplay(nodep, "-Info");
|
||||
|
|
@ -319,13 +319,13 @@ private:
|
|||
}
|
||||
|
||||
virtual void visit(AstNodePslCoverOrAssert* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
|
||||
newPslAssertion(nodep, nodep->propp(), nodep->sentreep(),
|
||||
nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstVAssert* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep);
|
||||
++m_statAsSV;
|
||||
}
|
||||
|
|
@ -333,7 +333,7 @@ private:
|
|||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
//
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Reset defaults
|
||||
m_modp = NULL;
|
||||
}
|
||||
|
|
@ -343,13 +343,13 @@ private:
|
|||
AstBegin* lastp = m_beginp;
|
||||
{
|
||||
m_beginp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_beginp = lastp;
|
||||
}
|
||||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -357,7 +357,7 @@ public:
|
|||
m_beginp = NULL;
|
||||
m_modp = NULL;
|
||||
// Process
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~AssertVisitor() {
|
||||
V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl);
|
||||
|
|
|
|||
|
|
@ -49,11 +49,7 @@ private:
|
|||
AstNodeSenItem* m_senip; // Last sensitivity
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
AstSenTree* newSenTree(AstNode* nodep) {
|
||||
// Create sentree based on clocked or default clock
|
||||
|
|
@ -89,12 +85,12 @@ private:
|
|||
virtual void visit(AstNodePslCoverOrAssert* nodep) {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
clearAssertInfo();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
clearAssertInfo();
|
||||
}
|
||||
virtual void visit(AstPslClocked* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (m_senip) {
|
||||
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
|
||||
}
|
||||
|
|
@ -112,12 +108,12 @@ private:
|
|||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Reset defaults
|
||||
m_seniDefaultp = NULL;
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -126,7 +122,7 @@ public:
|
|||
m_seniDefaultp = NULL;
|
||||
clearAssertInfo();
|
||||
// Process
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~AssertPreVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ int AstNodeDType::s_uniqueNum = 0;
|
|||
//######################################################################
|
||||
// V3AstType
|
||||
|
||||
ostream& operator<<(ostream& os, AstType rhs);
|
||||
std::ostream& operator<<(std::ostream& os, AstType rhs);
|
||||
|
||||
//######################################################################
|
||||
// Creators
|
||||
|
|
@ -290,8 +290,7 @@ void AstNode::addNextHere(AstNode* newp) {
|
|||
// Add to m_nextp on exact node passed, not at the end.
|
||||
// This could be at head, tail, or both (single)
|
||||
// New could be head of single node, or list
|
||||
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
|
||||
UASSERT(newp, "Null item passed to addNext");
|
||||
UASSERT(newp,"Null item passed to addNext");
|
||||
UASSERT(!newp->backp(), "New node (back) already assigned?");
|
||||
this->debugTreeChange("-addHereThs: ", __LINE__, false);
|
||||
newp->debugTreeChange("-addHereNew: ", __LINE__, true);
|
||||
|
|
@ -418,7 +417,7 @@ void AstNode::replaceWith(AstNode* newp) {
|
|||
repHandle.relink(newp);
|
||||
}
|
||||
|
||||
void AstNRelinker::dump(ostream& str) const {
|
||||
void AstNRelinker::dump(std::ostream& str) const {
|
||||
str<<" BK="<<(uint32_t*)m_backp;
|
||||
str<<" ITER="<<(uint32_t*)m_iterpp;
|
||||
str<<" CHG="<<(m_chg==RELINK_NEXT?"[NEXT] ":"");
|
||||
|
|
@ -646,7 +645,6 @@ AstNode* AstNode::cloneTreeIterList() {
|
|||
}
|
||||
|
||||
AstNode* AstNode::cloneTree(bool cloneNextLink) {
|
||||
if (!this) return NULL; // verilog.y relies on this
|
||||
this->debugTreeChange("-cloneThs: ", __LINE__, cloneNextLink);
|
||||
cloneClearTree();
|
||||
AstNode* newp;
|
||||
|
|
@ -802,7 +800,6 @@ void AstNode::iterateAndNext(AstNVisitor& v) {
|
|||
}
|
||||
|
||||
void AstNode::iterateListBackwards(AstNVisitor& v) {
|
||||
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
|
||||
AstNode* nodep=this;
|
||||
while (nodep->m_nextp) nodep=nodep->m_nextp;
|
||||
while (nodep) {
|
||||
|
|
@ -822,13 +819,13 @@ void AstNode::iterateChildrenBackwards(AstNVisitor& v) {
|
|||
|
||||
void AstNode::iterateAndNextConst(AstNVisitor& v) {
|
||||
// Keep following the current list even if edits change it
|
||||
if (!this) return; // A few cases could be cleaned up, but want symmetry with iterateAndNext
|
||||
for (AstNode* nodep=this; nodep; ) { // effectively: if (!this) return; // Callers rely on this
|
||||
AstNode* nodep=this;
|
||||
do {
|
||||
AstNode* nnextp = nodep->m_nextp;
|
||||
ASTNODE_PREFETCH(nnextp);
|
||||
nodep->accept(v);
|
||||
nodep = nnextp;
|
||||
}
|
||||
} while (nodep);
|
||||
}
|
||||
|
||||
AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
|
||||
|
|
@ -839,7 +836,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
|
|||
// To solve this, this function returns the pointer to the replacement node,
|
||||
// which in many cases is just the same node that was passed in.
|
||||
AstNode* nodep = this; // Note "this" may point to bogus point later in this function
|
||||
if (nodep->castNetlist()) {
|
||||
if (VN_IS(nodep, Netlist)) {
|
||||
// Calling on top level; we know the netlist won't get replaced
|
||||
nodep->accept(v);
|
||||
} else if (!nodep->backp()) {
|
||||
|
|
@ -886,7 +883,7 @@ void AstNode::cloneRelinkTree() {
|
|||
//======================================================================
|
||||
// Comparison
|
||||
|
||||
bool AstNode::gateTreeIter() {
|
||||
bool AstNode::gateTreeIter() const {
|
||||
// private: Return true if the two trees are identical
|
||||
if (!isGateOptimizable()) return false;
|
||||
if (m_op1p && !m_op1p->gateTreeIter()) return false;
|
||||
|
|
@ -896,7 +893,7 @@ bool AstNode::gateTreeIter() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool AstNode::sameTreeIter(AstNode* node1p, 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;
|
||||
|
|
@ -917,9 +914,9 @@ bool AstNode::sameTreeIter(AstNode* node1p, AstNode* node2p, bool ignNext, bool
|
|||
//======================================================================
|
||||
// Static utilities
|
||||
|
||||
ostream& operator<<(ostream& os, const V3Hash& rhs) {
|
||||
return os<<hex<<setw(2)<<setfill('0')<<rhs.depth()
|
||||
<<"_"<<setw(6)<<setfill('0')<<rhs.hshval();
|
||||
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();
|
||||
}
|
||||
|
||||
V3Hash::V3Hash(const string& name) {
|
||||
|
|
@ -938,7 +935,7 @@ void AstNode::checkTreeIter(AstNode* backp) {
|
|||
if (backp != this->backp()) {
|
||||
this->v3fatalSrc("Back node inconsistent");
|
||||
}
|
||||
if (castNodeTermop() || castNodeVarRef()) {
|
||||
if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) {
|
||||
// Termops have a short-circuited iterateChildren, so check usage
|
||||
if (op1p()||op2p()||op3p()||op4p())
|
||||
this->v3fatalSrc("Terminal operation with non-terminals");
|
||||
|
|
@ -975,17 +972,14 @@ void AstNode::checkTree() {
|
|||
}
|
||||
|
||||
void AstNode::dumpGdb() { // For GDB only
|
||||
if (!dynamic_cast<const AstNode*>(this)) { cout<<"This=NULL"<<endl; return; }
|
||||
dumpGdbHeader();
|
||||
cout<<" "; dump(cout); cout<<endl;
|
||||
}
|
||||
void AstNode::dumpGdbHeader() const { // For GDB only
|
||||
if (!dynamic_cast<const AstNode*>(this)) { cout<<"This=NULL"<<endl; return; }
|
||||
dumpPtrs(cout);
|
||||
cout<<" Fileline = "<<fileline()<<endl;
|
||||
}
|
||||
void AstNode::dumpTreeGdb() { // For GDB only
|
||||
if (!dynamic_cast<const AstNode*>(this)) { cout<<"This=NULL"<<endl; return; }
|
||||
dumpGdbHeader();
|
||||
dumpTree(cout);
|
||||
}
|
||||
|
|
@ -1002,7 +996,7 @@ void AstNode::checkIter() const {
|
|||
}
|
||||
}
|
||||
|
||||
void AstNode::dumpPtrs(ostream& os) const {
|
||||
void AstNode::dumpPtrs(std::ostream& os) const {
|
||||
os<<"This="<<typeName()<<" "<<(void*)this;
|
||||
os<<" back="<<(void*)backp();
|
||||
if (nextp()) os<<" next="<<(void*)nextp();
|
||||
|
|
@ -1024,7 +1018,7 @@ void AstNode::dumpPtrs(ostream& os) const {
|
|||
os<<endl;
|
||||
}
|
||||
|
||||
void AstNode::dumpTree(ostream& os, const string& indent, int maxDepth) {
|
||||
void AstNode::dumpTree(std::ostream& os, const string& indent, int maxDepth) {
|
||||
os<<indent<<" "<<this<<endl;
|
||||
if (debug()>8) { os<<indent<<" "; dumpPtrs(os); }
|
||||
if (maxDepth==1) {
|
||||
|
|
@ -1037,7 +1031,7 @@ void AstNode::dumpTree(ostream& os, const string& indent, int maxDepth) {
|
|||
}
|
||||
}
|
||||
|
||||
void AstNode::dumpTreeAndNext(ostream& os, const string& indent, int maxDepth) {
|
||||
void AstNode::dumpTreeAndNext(std::ostream& os, const string& indent, int maxDepth) {
|
||||
// Audited to make sure this is never NULL
|
||||
for (AstNode* nodep=this; nodep; nodep=nodep->nextp()) {
|
||||
nodep->dumpTree(os, indent, maxDepth);
|
||||
|
|
@ -1050,8 +1044,8 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump) {
|
|||
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"<<dec<<editCountLast()<<">";
|
||||
*logsp<<" to <e"<<dec<<editCountGbl()<<">"<<endl;
|
||||
*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;
|
||||
|
|
@ -1066,23 +1060,19 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump) {
|
|||
checkTree();
|
||||
// Broken isn't part of check tree because it can munge iterp's
|
||||
// set by other steps if it is called in the middle of other operations
|
||||
if (AstNetlist* netp=this->castNetlist()) V3Broken::brokenAll(netp);
|
||||
if (AstNetlist* netp=VN_CAST(this, Netlist)) V3Broken::brokenAll(netp);
|
||||
}
|
||||
// Next dump can indicate start from here
|
||||
editCountSetLast();
|
||||
}
|
||||
|
||||
void AstNode::v3errorEndFatal(ostringstream& str) const { v3errorEnd(str); assert(0); }
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const { v3errorEnd(str); assert(0); }
|
||||
|
||||
void AstNode::v3errorEnd(ostringstream& str) const {
|
||||
if (!dynamic_cast<const AstNode*>(this)) {
|
||||
// No known cases cause this, but better than a core dump
|
||||
if (debug()) UINFO(0, "-node: NULL. Please report this along with a --gdbbt backtrace as a Verilator bug.\n");
|
||||
V3Error::v3errorEnd(str);
|
||||
} else if (!m_fileline) {
|
||||
void AstNode::v3errorEnd(std::ostringstream& str) const {
|
||||
if (!m_fileline) {
|
||||
V3Error::v3errorEnd(str);
|
||||
} else {
|
||||
ostringstream nsstr;
|
||||
std::ostringstream nsstr;
|
||||
nsstr<<str.str();
|
||||
if (debug()) {
|
||||
nsstr<<endl;
|
||||
|
|
@ -1153,7 +1143,7 @@ AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) {
|
|||
// AstNVisitor
|
||||
|
||||
void AstNVisitor::doDeletes() {
|
||||
for (vector<AstNode*>::iterator it = m_deleteps.begin(); it != m_deleteps.end(); ++it) {
|
||||
for (std::vector<AstNode*>::iterator it = m_deleteps.begin(); it != m_deleteps.end(); ++it) {
|
||||
(*it)->deleteTree();
|
||||
}
|
||||
m_deleteps.clear();
|
||||
|
|
|
|||
204
src/V3Ast.h
204
src/V3Ast.h
|
|
@ -29,19 +29,36 @@
|
|||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include VL_INCLUDE_UNORDERED_SET
|
||||
|
||||
#include "V3Ast__gen_classes.h" // From ./astgen
|
||||
// Things like:
|
||||
// class V3AstNode;
|
||||
|
||||
// Forward declarations
|
||||
class V3Graph;
|
||||
class ExecMTask;
|
||||
|
||||
// Hint class so we can choose constructors
|
||||
class VFlagLogicPacked {};
|
||||
class VFlagBitPacked {};
|
||||
class VFlagChildDType {}; // Used by parser.y to select constructor that sets childDType
|
||||
|
||||
// Used as key for another map, needs operator<, hence not an unordered_set
|
||||
typedef std::set<int> MTaskIdSet; // Set of mtaskIds for Var sorting
|
||||
|
||||
//######################################################################
|
||||
|
||||
// For broken() function, return error string if have a match
|
||||
#define BROKEN_RTN(test) do { if (VL_UNLIKELY(test)) return # test; } while(0)
|
||||
|
||||
// (V)erilator (N)ode is: True if AstNode is of a a given AstType
|
||||
#define VN_IS(nodep,nodetypename) (AstNode::privateIs ## nodetypename(nodep))
|
||||
|
||||
// (V)erilator (N)ode cast: Cast to given type if can; effectively dynamic_cast(nodep)(nodetypename)
|
||||
#define VN_CAST(nodep,nodetypename) (AstNode::privateCast ## nodetypename(nodep))
|
||||
#define VN_CAST_CONST(nodep,nodetypename) (AstNode::privateConstCast ## nodetypename(nodep) )
|
||||
|
||||
//######################################################################
|
||||
|
||||
class AstType {
|
||||
|
|
@ -61,7 +78,7 @@ public:
|
|||
inline bool operator== (AstType lhs, AstType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstType lhs, AstType::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstType::en lhs, AstType rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstType& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstType& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
|
@ -107,7 +124,7 @@ public:
|
|||
inline bool operator== (AstNumeric lhs, AstNumeric rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstNumeric lhs, AstNumeric::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstNumeric::en lhs, AstNumeric rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstNumeric& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstNumeric& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
|
@ -497,7 +514,7 @@ public:
|
|||
inline bool operator== (AstVarType lhs, AstVarType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstVarType lhs, AstVarType::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstVarType::en lhs, AstVarType rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstVarType& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstVarType& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
|
@ -529,7 +546,7 @@ public:
|
|||
inline bool operator== (AstBranchPred lhs, AstBranchPred rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstBranchPred lhs, AstBranchPred::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstBranchPred::en lhs, AstBranchPred rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstBranchPred& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstBranchPred& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
|
@ -562,7 +579,7 @@ public:
|
|||
inline bool operator== (AstVarAttrClocker lhs, AstVarAttrClocker rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstVarAttrClocker lhs, AstVarAttrClocker::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstVarAttrClocker::en lhs, AstVarAttrClocker rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstVarAttrClocker& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstVarAttrClocker& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
|
@ -661,7 +678,7 @@ public:
|
|||
inline bool operator== (AstParseRefExp lhs, AstParseRefExp rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstParseRefExp lhs, AstParseRefExp::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstParseRefExp::en lhs, AstParseRefExp rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, const AstParseRefExp& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstParseRefExp& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
// VNumRange - Structure containing numberic range information
|
||||
|
|
@ -715,9 +732,9 @@ struct VNumRange {
|
|||
int hiMaxSelect() const { return (lo()<0 ? hi()-lo() : hi()); } // Maximum value a [] select may index
|
||||
bool representableByWidth() const // Could be represented by just width=1, or [width-1:0]
|
||||
{ return (!m_ranged || (m_lo==0 && m_hi>=1 && !m_littleEndian)); }
|
||||
void dump(ostream& str) const { if (ranged()) str<<"["<<left()<<":"<<right()<<"]"; else str<<"[norg]"; }
|
||||
void dump(std::ostream& str) const { if (ranged()) str<<"["<<left()<<":"<<right()<<"]"; else str<<"[norg]"; }
|
||||
};
|
||||
inline ostream& operator<<(ostream& os, const VNumRange& rhs) { rhs.dump(os); return os; }
|
||||
inline std::ostream& operator<<(std::ostream& os, const VNumRange& rhs) { rhs.dump(os); return os; }
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
|
@ -891,10 +908,12 @@ public:
|
|||
|
||||
class AstNVisitor {
|
||||
private:
|
||||
vector<AstNode*> m_deleteps; // Nodes to delete when doDeletes() called
|
||||
// MEMBERS
|
||||
std::vector<AstNode*> m_deleteps; // Nodes to delete when doDeletes() called
|
||||
protected:
|
||||
friend class AstNode;
|
||||
public:
|
||||
// METHODS
|
||||
/// At the end of the visitor (or doDeletes()), delete this pushed node
|
||||
/// along with all children and next(s). This is often better to use
|
||||
/// than an immediate deleteTree, as any pointers into this node will
|
||||
|
|
@ -908,6 +927,21 @@ public:
|
|||
virtual ~AstNVisitor() {
|
||||
doDeletes();
|
||||
}
|
||||
/// Call visit()s on nodep
|
||||
void iterate(AstNode* nodep);
|
||||
/// Call visit()s on nodep's children
|
||||
void iterateChildren(AstNode* nodep);
|
||||
/// Call visit()s on nodep's children in backp() order
|
||||
void iterateChildrenBackwards(AstNode* nodep);
|
||||
/// Call visit()s on const nodep's children
|
||||
void iterateChildrenConst(AstNode* nodep);
|
||||
/// Call visit()s on nodep (maybe NULL) and nodep's nextp() list
|
||||
void iterateAndNextNull(AstNode* nodep);
|
||||
/// Call visit()s on const nodep (maybe NULL) and nodep's nextp() list
|
||||
void iterateAndNextConstNull(AstNode* nodep);
|
||||
/// Return edited nodep; see comments in V3Ast.cpp
|
||||
AstNode* iterateSubtreeReturnEdits(AstNode* nodep);
|
||||
|
||||
#include "V3Ast__gen_visitor.h" // From ./astgen
|
||||
// Things like:
|
||||
// virtual void visit(AstBreak* nodep) { visit((AstNodeStmt*)(nodep)); }
|
||||
|
|
@ -932,9 +966,9 @@ public:
|
|||
AstNRelinker() { m_oldp=NULL; m_backp=NULL; m_chg=RELINK_BAD; m_iterpp=NULL;}
|
||||
void relink(AstNode* newp);
|
||||
AstNode* oldp() const { return m_oldp; }
|
||||
void dump(ostream& str=cout) const;
|
||||
void dump(std::ostream& str=std::cout) const;
|
||||
};
|
||||
inline ostream& operator<<(ostream& os, const AstNRelinker& rhs) { rhs.dump(os); return os;}
|
||||
inline std::ostream& operator<<(std::ostream& os, const AstNRelinker& rhs) { rhs.dump(os); return os;}
|
||||
|
||||
//######################################################################
|
||||
// V3Hash -- Node hashing for V3Combine
|
||||
|
|
@ -984,7 +1018,7 @@ public:
|
|||
V3Hash(V3Hash h1, V3Hash h2, V3Hash h3, V3Hash h4) {
|
||||
setBoth(1,((h1.hshval()*31+h2.hshval())*31+h3.hshval())*31+h4.hshval()); }
|
||||
};
|
||||
ostream& operator<<(ostream& os, const V3Hash& rhs);
|
||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs);
|
||||
|
||||
//######################################################################
|
||||
// AstNode -- Base type of all Ast types
|
||||
|
|
@ -1042,14 +1076,13 @@ class AstNode {
|
|||
void op4p(AstNode* nodep) { m_op4p = nodep; if (nodep) nodep->m_backp = this; }
|
||||
|
||||
void init(); // initialize value of AstNode
|
||||
void iterateListBackwards(AstNVisitor& v);
|
||||
private:
|
||||
AstNode* cloneTreeIter();
|
||||
AstNode* cloneTreeIterList();
|
||||
void checkTreeIter(AstNode* backp);
|
||||
void checkTreeIterList(AstNode* backp);
|
||||
bool gateTreeIter();
|
||||
bool sameTreeIter(AstNode* node1p, AstNode* node2p, bool ignNext, bool gateOnly);
|
||||
bool gateTreeIter() const;
|
||||
static bool sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext, bool gateOnly);
|
||||
void deleteTreeIter();
|
||||
void deleteNode();
|
||||
public:
|
||||
|
|
@ -1241,11 +1274,11 @@ public:
|
|||
|
||||
// ACCESSORS for specific types
|
||||
// Alas these can't be virtual or they break when passed a NULL
|
||||
bool isZero();
|
||||
bool isOne();
|
||||
bool isNeqZero();
|
||||
bool isAllOnes();
|
||||
bool isAllOnesV(); // Verilog width rules apply
|
||||
bool isZero() const;
|
||||
bool isOne() const;
|
||||
bool isNeqZero() const;
|
||||
bool isAllOnes() const;
|
||||
bool isAllOnesV() const; // Verilog width rules apply
|
||||
|
||||
// METHODS - data type changes especially for initial creation
|
||||
void dtypep(AstNodeDType* nodep) { if (m_dtypep != nodep) { m_dtypep = nodep; editCountInc(); } }
|
||||
|
|
@ -1276,10 +1309,10 @@ public:
|
|||
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
|
||||
// METHODS - dump and error
|
||||
void v3errorEnd(ostringstream& str) const;
|
||||
void v3errorEndFatal(ostringstream& str) const VL_ATTR_NORETURN;
|
||||
void v3errorEnd(std::ostringstream& str) const;
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN;
|
||||
string warnMore() const;
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
void dumpGdb(); // For GDB only
|
||||
void dumpGdbHeader() const;
|
||||
|
||||
|
|
@ -1303,19 +1336,21 @@ public:
|
|||
virtual void addBeforeStmt(AstNode* newp, AstNode* belowp); // When calling, "this" is second argument
|
||||
|
||||
// METHODS - Iterate on a tree
|
||||
static AstNode* cloneTreeNull(AstNode* nodep, bool cloneNextLink) { // Clone or return NULL if NULL
|
||||
return nodep ? nodep->cloneTree(cloneNextLink) : NULL; }
|
||||
AstNode* cloneTree(bool cloneNextLink);
|
||||
bool gateTree() { return gateTreeIter(); } // Is tree isGateOptimizable?
|
||||
bool sameTree(AstNode* node2p); // Does tree of this == node2p?
|
||||
bool sameGateTree(AstNode* node2p); // Does tree of this == node2p?, not allowing non-isGateOptimizable
|
||||
bool sameTree(const AstNode* node2p) const; // Does tree of this == node2p?
|
||||
bool sameGateTree(const AstNode* node2p) const; // Does tree of this == node2p?, not allowing non-isGateOptimizable
|
||||
void deleteTree(); // Always deletes the next link
|
||||
void checkTree(); // User Interface version
|
||||
void checkIter() const;
|
||||
void clearIter() { m_iterpp=NULL; }
|
||||
void dumpPtrs(ostream& str=cout) const;
|
||||
void dumpTree(ostream& str=cout, const string& indent=" ", int maxDepth=0);
|
||||
void dumpPtrs(std::ostream& str=std::cout) const;
|
||||
void dumpTree(std::ostream& str=std::cout, const string& indent=" ", int maxDepth=0);
|
||||
void dumpTree(const string& indent, int maxDepth=0) { dumpTree(cout,indent,maxDepth); }
|
||||
void dumpTreeGdb(); // For GDB only
|
||||
void dumpTreeAndNext(ostream& str=cout, const string& indent=" ", int maxDepth=0);
|
||||
void dumpTreeAndNext(std::ostream& str=std::cout, const string& indent=" ", int maxDepth=0);
|
||||
void dumpTreeFile(const string& filename, bool append=false, bool doDump=true);
|
||||
static void dumpTreeFileGdb(const char* filenamep=NULL);
|
||||
|
||||
|
|
@ -1341,21 +1376,27 @@ public:
|
|||
|
||||
// INVOKERS
|
||||
virtual void accept(AstNVisitor& v) = 0;
|
||||
void iterate(AstNVisitor& v) { this->accept(v); } // Does this; excludes following this->next
|
||||
void iterateAndNext(AstNVisitor& v);
|
||||
void iterateAndNextConst(AstNVisitor& v);
|
||||
void iterateChildren(AstNVisitor& v); // Excludes following this->next
|
||||
void iterateChildrenBackwards(AstNVisitor& v); // Excludes following this->next
|
||||
void iterateChildrenConst(AstNVisitor& v); // Excludes following this->next
|
||||
AstNode* iterateSubtreeReturnEdits(AstNVisitor& v); // Return edited nodep; see comments in V3Ast.cpp
|
||||
|
||||
protected:
|
||||
// All AstNVisitor related functions are called as methods off the visitor
|
||||
friend class AstNVisitor;
|
||||
void iterateChildren(AstNVisitor& v); // Use instead AstNVisitor::iterateChildren
|
||||
void iterateChildrenBackwards(AstNVisitor& v); // Use instead AstNVisitor::iterateChildrenBackwards
|
||||
void iterateChildrenConst(AstNVisitor& v); // Use instead AstNVisitor::iterateChildrenConst
|
||||
void iterateAndNext(AstNVisitor& v); // Use instead AstNVisitor::iterateAndNextNull
|
||||
void iterateAndNextConst(AstNVisitor& v); // Use instead AstNVisitor::iterateAndNextConstNull
|
||||
AstNode* iterateSubtreeReturnEdits(AstNVisitor& v); // Use instead AstNVisitor::iterateSubtreeReturnEdits
|
||||
private:
|
||||
void iterateListBackwards(AstNVisitor& v);
|
||||
|
||||
// CONVERSION
|
||||
public:
|
||||
#include "V3Ast__gen_interface.h" // From ./astgen
|
||||
// Things like:
|
||||
// AstAlways* castAlways();
|
||||
};
|
||||
|
||||
inline ostream& operator<<(ostream& os, AstNode* rhs) { if (!rhs) os<<"NULL"; else rhs->dump(os); return os; }
|
||||
inline std::ostream& operator<<(std::ostream& os, AstNode* rhs) { if (!rhs) os<<"NULL"; else rhs->dump(os); return os; }
|
||||
inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -1364,6 +1405,8 @@ inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
|
|||
|
||||
#define ASTNODE_BASE_FUNCS(name) \
|
||||
virtual ~Ast ##name() {} \
|
||||
static Ast ##name * cloneTreeNull(Ast ##name * nodep, bool cloneNextLink) { \
|
||||
return nodep ? nodep->cloneTree(cloneNextLink) : NULL; } \
|
||||
Ast ##name * cloneTree(bool cloneNext) { return static_cast<Ast ##name *>(AstNode::cloneTree(cloneNext)); } \
|
||||
Ast ##name * clonep() const { return static_cast<Ast ##name *>(AstNode::clonep()); }
|
||||
|
||||
|
|
@ -1381,7 +1424,7 @@ public:
|
|||
virtual bool cleanOut() = 0; // True if output has extra upper bits zero
|
||||
// Someday we will generically support data types on every math node
|
||||
// Until then isOpaque indicates we shouldn't constant optimize this node type
|
||||
bool isOpaque() { return castCvtPackString()!=NULL; }
|
||||
bool isOpaque() { return VN_IS(this, CvtPackString); }
|
||||
};
|
||||
|
||||
class AstNodeTermop : public AstNodeMath {
|
||||
|
|
@ -1523,7 +1566,7 @@ public:
|
|||
AstNode* fromp() const { return lhsp(); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
AstNode* thsp() const { return op3p(); }
|
||||
AstAttrOf* attrp() const { return op4p()->castAttrOf(); }
|
||||
AstAttrOf* attrp() const { return VN_CAST(op4p(), AttrOf); }
|
||||
void lhsp(AstNode* nodep) { return setOp1p(nodep); }
|
||||
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
|
||||
void thsp(AstNode* nodep) { return setOp3p(nodep); }
|
||||
|
|
@ -1618,7 +1661,7 @@ public:
|
|||
ASTNODE_BASE_FUNCS(NodeCase)
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
AstNode* exprp() const { return op1p(); } // op1 = case condition <expression>
|
||||
AstCaseItem* itemsp() const { return op2p()->castCaseItem(); } // op2 = list of case expressions
|
||||
AstCaseItem* itemsp() const { return VN_CAST(op2p(), CaseItem); } // op2 = list of case expressions
|
||||
AstNode* notParallelp() const { return op3p(); } // op3 = assertion code for non-full case's
|
||||
void addItemsp(AstNode* nodep) { addOp2p(nodep); }
|
||||
void addNotParallelp(AstNode* nodep) { setOp3p(nodep); }
|
||||
|
|
@ -1694,7 +1737,7 @@ public:
|
|||
m_text = textp; // Copy it
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeText)
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
virtual V3Hash sameHash() const { return V3Hash(text()); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstNodeText* asamep = static_cast<const AstNodeText*>(samep);
|
||||
|
|
@ -1720,8 +1763,8 @@ public:
|
|||
}
|
||||
ASTNODE_BASE_FUNCS(NodeDType)
|
||||
// ACCESSORS
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dumpSmall(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual void dumpSmall(std::ostream& str);
|
||||
virtual bool hasDType() const { return true; }
|
||||
virtual AstBasicDType* basicp() const = 0; // (Slow) recurse down to find basic data type
|
||||
virtual AstNodeDType* skipRefp() const = 0; // recurses over typedefs/const/enum to next non-typeref type
|
||||
|
|
@ -1755,7 +1798,7 @@ public:
|
|||
bool generic() const { return m_generic; }
|
||||
void generic(bool flag) { m_generic = flag; }
|
||||
AstNodeDType* dtypeDimensionp(int depth);
|
||||
pair<uint32_t,uint32_t> dimensions(bool includeBasic);
|
||||
std::pair<uint32_t,uint32_t> dimensions(bool includeBasic);
|
||||
uint32_t arrayUnpackedElements(); // 1, or total multiplication of all dimensions
|
||||
static int uniqueNumInc() { return ++s_uniqueNum; }
|
||||
const char* charIQWN() const { return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I"); }
|
||||
|
|
@ -1764,7 +1807,7 @@ public:
|
|||
class AstNodeClassDType : public AstNodeDType {
|
||||
private:
|
||||
// TYPES
|
||||
typedef map<string,AstMemberDType*> MemberNameMap;
|
||||
typedef std::map<string,AstMemberDType*> MemberNameMap;
|
||||
// MEMBERS
|
||||
bool m_packed;
|
||||
bool m_isFourstate;
|
||||
|
|
@ -1779,11 +1822,12 @@ public:
|
|||
}
|
||||
ASTNODE_BASE_FUNCS(NodeClassDType)
|
||||
virtual const char* broken() const;
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
// For basicp() we reuse the size to indicate a "fake" basic type of same size
|
||||
virtual AstBasicDType* basicp() const {
|
||||
return (isFourstate() ? findLogicDType(width(),width(),numeric())->castBasicDType()
|
||||
: findBitDType(width(),width(),numeric())->castBasicDType()); }
|
||||
return (isFourstate()
|
||||
? VN_CAST(findLogicDType(width(),width(),numeric()), BasicDType)
|
||||
: VN_CAST(findBitDType(width(),width(),numeric()), BasicDType)); }
|
||||
virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; }
|
||||
|
|
@ -1793,7 +1837,7 @@ public:
|
|||
virtual bool similarDType(AstNodeDType* samep) const {
|
||||
return this==samep; // We don't compare members, require exact equivalence
|
||||
}
|
||||
AstMemberDType* membersp() const { return op1p()->castMemberDType(); } // op1 = AstMember list
|
||||
AstMemberDType* membersp() const { return VN_CAST(op1p(), MemberDType); } // op1 = AstMember list
|
||||
void addMembersp(AstNode* nodep) { addNOp1p(nodep); }
|
||||
bool packed() const { return m_packed; }
|
||||
bool packedUnsup() const { return true; } // packed() but as don't support unpacked, presently all structs
|
||||
|
|
@ -1822,8 +1866,8 @@ public:
|
|||
m_refDTypep = NULL;
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeArrayDType)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dumpSmall(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual void dumpSmall(std::ostream& str);
|
||||
virtual const char* broken() const { BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep()))); return NULL; }
|
||||
virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) {
|
||||
|
|
@ -1844,13 +1888,13 @@ public:
|
|||
}
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep),V3Hash(msb()),V3Hash(lsb())); }
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; }
|
||||
virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); }
|
||||
AstRange* rangep() const { return op2p()->castRange(); } // op2 = Array(s) of variable
|
||||
AstRange* rangep() const { return VN_CAST(op2p(), Range); } // op2 = Array(s) of variable
|
||||
void rangep(AstRange* nodep);
|
||||
// METHODS
|
||||
virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type
|
||||
|
|
@ -1919,7 +1963,7 @@ public:
|
|||
cname(name); // Might be overridden by dpi import/export
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeFTask)
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual bool isGateOptimizable() const { return !((m_dpiExport || m_dpiImport) && !m_pure); }
|
||||
|
|
@ -1935,7 +1979,7 @@ public:
|
|||
AstNode* stmtsp() const { return op3p(); } // op3 = List of statements
|
||||
void addStmtsp(AstNode* nodep) { addNOp3p(nodep); }
|
||||
// op4 = scope name
|
||||
AstScopeName* scopeNamep() const { return op4p()->castScopeName(); }
|
||||
AstScopeName* scopeNamep() const { return VN_CAST(op4p(), ScopeName); }
|
||||
// MORE ACCESSORS
|
||||
void dpiOpenParentInc() { ++m_dpiOpenParent; }
|
||||
void dpiOpenParentClear() { m_dpiOpenParent=0; }
|
||||
|
|
@ -1985,7 +2029,7 @@ public:
|
|||
virtual void cloneRelink() { if (m_taskp && m_taskp->clonep()) {
|
||||
m_taskp = m_taskp->clonep();
|
||||
}}
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual bool isGateOptimizable() const { return m_taskp && m_taskp->isGateOptimizable(); }
|
||||
string dotted() const { return m_dotted; } // * = Scope name or ""
|
||||
|
|
@ -2004,7 +2048,7 @@ public:
|
|||
AstNode* pinsp() const { return op2p(); }
|
||||
void addPinsp(AstNode* nodep) { addOp2p(nodep); }
|
||||
// op3 = scope tracking
|
||||
AstScopeName* scopeNamep() const { return op3p()->castScopeName(); }
|
||||
AstScopeName* scopeNamep() const { return VN_CAST(op3p(), ScopeName); }
|
||||
void scopeNamep(AstNode* nodep) { setNOp3p(nodep); }
|
||||
};
|
||||
|
||||
|
|
@ -2033,11 +2077,11 @@ public:
|
|||
,m_internal(false), m_recursive(false), m_recursiveClone(false)
|
||||
,m_level(0), m_varNum(0), m_typeNum(0) { }
|
||||
ASTNODE_BASE_FUNCS(NodeModule)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual string name() const { return m_name; }
|
||||
AstNode* stmtsp() const { return op2p(); } // op2 = List of statements
|
||||
AstActive* activesp() const { return op3p()->castActive(); } // op3 = List of i/sblocks
|
||||
AstActive* activesp() const { return VN_CAST(op3p(), Active); } // op3 = List of i/sblocks
|
||||
// METHODS
|
||||
void addInlinesp(AstNode* nodep) { addOp1p(nodep); }
|
||||
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
|
||||
|
|
@ -2079,7 +2123,33 @@ public:
|
|||
|
||||
#include "V3Ast__gen_impl.h" // From ./astgen
|
||||
// Things like:
|
||||
// inline AstAlways* AstNode::castAlways() { return dynamic_cast<AstAlways*>(this);}
|
||||
// inline AstAlways* AstNode::castAlways() { return dynamic_cast<AstAlways*>(this);}
|
||||
// inline bool AstNode::privateIsaAlways(const AstNode* nodep) { return nodep && nodep->type() == AstType::atAlways; }
|
||||
|
||||
//######################################################################
|
||||
// Inline AstNVisitor METHODS
|
||||
|
||||
inline void AstNVisitor::iterate(AstNode* nodep) {
|
||||
nodep->accept(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateChildren(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateChildrenBackwards(AstNode* nodep) {
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateChildrenConst(AstNode* nodep) {
|
||||
nodep->iterateChildrenConst(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateAndNextNull(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->iterateAndNext(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateAndNextConstNull(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->iterateAndNextConst(*this);
|
||||
}
|
||||
inline AstNode* AstNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) {
|
||||
return nodep->iterateSubtreeReturnEdits(*this);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Inline ACCESSORS
|
||||
|
|
@ -2091,19 +2161,19 @@ inline bool AstNode::width1() const { // V3Const uses to know it can optimize
|
|||
inline int AstNode::widthInstrs() const {
|
||||
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1)); }
|
||||
inline bool AstNode::isDouble() const {
|
||||
return dtypep() && dtypep()->castBasicDType() && dtypep()->castBasicDType()->isDouble(); }
|
||||
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_CAST(dtypep(), BasicDType)->isDouble(); }
|
||||
inline bool AstNode::isString() const {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString(); }
|
||||
inline bool AstNode::isSigned() const {
|
||||
return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
inline bool AstNode::isZero() { return (this->castConst() && this->castConst()->num().isEqZero()); }
|
||||
inline bool AstNode::isNeqZero() { return (this->castConst() && this->castConst()->num().isNeqZero()); }
|
||||
inline bool AstNode::isOne() { return (this->castConst() && this->castConst()->num().isEqOne()); }
|
||||
inline bool AstNode::isAllOnes() { return (this->castConst() && this->castConst()->isEqAllOnes()); }
|
||||
inline bool AstNode::isAllOnesV() { return (this->castConst() && this->castConst()->isEqAllOnesV()); }
|
||||
inline bool AstNode::sameTree(AstNode* node2p) { return sameTreeIter(this, node2p, true, false); }
|
||||
inline bool AstNode::sameGateTree(AstNode* node2p) { return sameTreeIter(this, node2p, true, true); }
|
||||
inline bool AstNode::isZero() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqZero()); }
|
||||
inline bool AstNode::isNeqZero() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isNeqZero()); }
|
||||
inline bool AstNode::isOne() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqOne()); }
|
||||
inline bool AstNode::isAllOnes() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnes()); }
|
||||
inline bool AstNode::isAllOnesV() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnesV()); }
|
||||
inline bool AstNode::sameTree(const AstNode* node2p) const { return sameTreeIter(this, node2p, true, false); }
|
||||
inline bool AstNode::sameGateTree(const AstNode* node2p) const { return sameTreeIter(this, node2p, true, true); }
|
||||
|
||||
inline void AstNodeVarRef::init() { if (m_varp) dtypep(m_varp->dtypep()); }
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
#include "V3Ast.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3PartitionGraph.h" // Just for mtask dumping
|
||||
|
||||
//======================================================================
|
||||
// Special methods
|
||||
|
|
@ -44,7 +46,7 @@ const char* AstIfaceRefDType::broken() const {
|
|||
}
|
||||
|
||||
AstIface* AstIfaceRefDType::ifaceViaCellp() const {
|
||||
return ((m_cellp && m_cellp->modp()) ? m_cellp->modp()->castIface() : m_ifacep);
|
||||
return ((m_cellp && m_cellp->modp()) ? VN_CAST(m_cellp->modp(), Iface) : m_ifacep);
|
||||
}
|
||||
|
||||
const char* AstNodeVarRef::broken() const {
|
||||
|
|
@ -58,12 +60,13 @@ void AstNodeVarRef::cloneRelink() {
|
|||
}
|
||||
|
||||
int AstNodeSel::bitConst() const {
|
||||
AstConst* constp=bitp()->castConst(); return (constp?constp->toSInt():0);
|
||||
AstConst* constp=VN_CAST(bitp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
void AstNodeClassDType::repairMemberCache() {
|
||||
clearCache();
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) {
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
if (m_members.find(itemp->name())!=m_members.end()) {
|
||||
itemp->v3error("Duplicate declaration of member name: "<<itemp->prettyName()); }
|
||||
else m_members.insert(make_pair(itemp->name(), itemp));
|
||||
|
|
@ -72,7 +75,7 @@ void AstNodeClassDType::repairMemberCache() {
|
|||
|
||||
const char* AstNodeClassDType::broken() const {
|
||||
vl_unordered_set<AstMemberDType*> exists;
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) {
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
exists.insert(itemp);
|
||||
}
|
||||
for (MemberNameMap::const_iterator it=m_members.begin(); it!=m_members.end(); ++it) {
|
||||
|
|
@ -150,22 +153,26 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
|||
}
|
||||
}
|
||||
|
||||
AstExecGraph::AstExecGraph(FileLine* fileline)
|
||||
: AstNode(fileline) {
|
||||
m_depGraphp = new V3Graph;
|
||||
}
|
||||
AstExecGraph::~AstExecGraph() {
|
||||
delete m_depGraphp; VL_DANGLING(m_depGraphp);
|
||||
}
|
||||
|
||||
bool AstVar::isSigPublic() const {
|
||||
return (m_sigPublic || (v3Global.opt.allPublic() && !isTemp() && !isGenVar()));
|
||||
}
|
||||
|
||||
bool AstVar::isScQuad() const {
|
||||
return (isSc() && isQuad() && !isScBv() && !isScBigUint());
|
||||
}
|
||||
|
||||
bool AstVar::isScBv() const {
|
||||
return ((isSc() && width() >= v3Global.opt.pinsBv()) || m_attrScBv);
|
||||
}
|
||||
|
||||
bool AstVar::isScUint() const {
|
||||
return ((isSc() && v3Global.opt.pinsScUint() && width() >= 2 && width() <= 64) && !isScBv());
|
||||
}
|
||||
|
||||
bool AstVar::isScBigUint() const {
|
||||
return ((isSc() && v3Global.opt.pinsScBigUint() && width() >= 65 && width() <= 512) && !isScBv());
|
||||
}
|
||||
|
|
@ -244,7 +251,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const {
|
|||
arg += " (& "+name()+")";
|
||||
for (AstNodeDType* dtp=dtypep(); dtp; ) {
|
||||
dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstUnpackArrayDType* adtypep = dtp->castUnpackArrayDType()) {
|
||||
if (AstUnpackArrayDType* adtypep = VN_CAST(dtp, UnpackArrayDType)) {
|
||||
arg += "["+cvtToStr(adtypep->declRange().elements())+"]";
|
||||
dtp = adtypep->subDTypep();
|
||||
} else break;
|
||||
|
|
@ -316,7 +323,7 @@ string AstVar::vlPropInit() const {
|
|||
bool first = true;
|
||||
for (AstNodeDType* dtp=dtypep(); dtp; ) {
|
||||
dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstNodeArrayDType* adtypep = dtp->castNodeArrayDType()) {
|
||||
if (AstNodeArrayDType* adtypep = VN_CAST(dtp, NodeArrayDType)) {
|
||||
if (first) {
|
||||
out += ", VerilatedVarProps::Unpacked()";
|
||||
first = false;
|
||||
|
|
@ -423,15 +430,15 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
|
|||
// See if this is a SC assignment; if so return that type
|
||||
// Historically sc variables are identified by a variable
|
||||
// attribute. TODO it would better be a data type attribute.
|
||||
if (AstVar* anodep = nodep->castVar()) {
|
||||
if (AstVar* anodep = VN_CAST(nodep, Var)) {
|
||||
if (anodep->isSc()) return anodep;
|
||||
else return NULL;
|
||||
}
|
||||
else if (nodep->castVarRef()) {
|
||||
if (nodep->castVarRef()->varp()->isSc()) return nodep->castVarRef()->varp();
|
||||
else if (VN_IS(nodep, VarRef)) {
|
||||
if (VN_CAST(nodep, VarRef)->varp()->isSc()) return VN_CAST(nodep, VarRef)->varp();
|
||||
else return NULL;
|
||||
}
|
||||
else if (nodep->castArraySel()) {
|
||||
else if (VN_IS(nodep, ArraySel)) {
|
||||
if (nodep->op1p()) if (AstVar* p = scVarRecurse(nodep->op1p())) return p;
|
||||
if (nodep->op2p()) if (AstVar* p = scVarRecurse(nodep->op2p())) return p;
|
||||
if (nodep->op3p()) if (AstVar* p = scVarRecurse(nodep->op3p())) return p;
|
||||
|
|
@ -440,6 +447,16 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
string AstVar::mtasksString() const {
|
||||
std::ostringstream os;
|
||||
os<<" all: ";
|
||||
for (MTaskIdSet::const_iterator it = m_mtaskIds.begin();
|
||||
it != m_mtaskIds.end(); ++it) {
|
||||
os<<*it<<" ";
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
|
||||
// dimension passed from AstArraySel::dimension
|
||||
// Dimension 0 means the VAR itself, 1 is the closest SEL to the AstVar,
|
||||
|
|
@ -455,17 +472,16 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
|
|||
// TODO this function should be removed in favor of recursing the dtype(),
|
||||
// as that allows for more complicated data types.
|
||||
int dim = 0;
|
||||
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
|
||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstNodeArrayDType* adtypep = dtypep->castNodeArrayDType()) {
|
||||
if (AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||
if ((dim++)==dimension) {
|
||||
return dtypep;
|
||||
}
|
||||
dtypep = adtypep->subDTypep();
|
||||
continue;
|
||||
}
|
||||
else if (AstBasicDType* adtypep = dtypep->castBasicDType()) {
|
||||
else if (AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
// AstBasicDType - nothing below, return null
|
||||
if (adtypep->isRanged()) {
|
||||
if ((dim++) == dimension) {
|
||||
|
|
@ -474,7 +490,7 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
|
|||
}
|
||||
return NULL;
|
||||
}
|
||||
else if (AstNodeClassDType* adtypep = dtypep->castNodeClassDType()) {
|
||||
else if (AstNodeClassDType* adtypep = VN_CAST(dtypep, NodeClassDType)) {
|
||||
if (adtypep->packed()) {
|
||||
if ((dim++) == dimension) {
|
||||
return adtypep;
|
||||
|
|
@ -490,10 +506,9 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) {
|
|||
|
||||
uint32_t AstNodeDType::arrayUnpackedElements() {
|
||||
uint32_t entries=1;
|
||||
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
|
||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstUnpackArrayDType* adtypep = dtypep->castUnpackArrayDType()) {
|
||||
if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
entries *= adtypep->elementsConst();
|
||||
dtypep = adtypep->subDTypep();
|
||||
}
|
||||
|
|
@ -505,20 +520,19 @@ uint32_t AstNodeDType::arrayUnpackedElements() {
|
|||
return entries;
|
||||
}
|
||||
|
||||
pair<uint32_t,uint32_t> AstNodeDType::dimensions(bool includeBasic) {
|
||||
std::pair<uint32_t,uint32_t> AstNodeDType::dimensions(bool includeBasic) {
|
||||
// How many array dimensions (packed,unpacked) does this Var have?
|
||||
uint32_t packed = 0;
|
||||
uint32_t unpacked = 0;
|
||||
UDEBUGONLY(UASSERT(dynamic_cast<AstNode*>(this),"this should not be NULL"););
|
||||
for (AstNodeDType* dtypep=this; dtypep; ) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstNodeArrayDType* adtypep = dtypep->castNodeArrayDType()) {
|
||||
if (adtypep->castPackArrayDType()) packed++;
|
||||
if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||
if (VN_IS(adtypep, PackArrayDType)) packed++;
|
||||
else unpacked++;
|
||||
dtypep = adtypep->subDTypep();
|
||||
continue;
|
||||
}
|
||||
else if (AstBasicDType* adtypep = dtypep->castBasicDType()) {
|
||||
else if (const AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
if (includeBasic && adtypep->isRanged()) packed++;
|
||||
}
|
||||
break;
|
||||
|
|
@ -538,15 +552,15 @@ int AstNodeDType::widthPow2() const {
|
|||
AstNode* AstArraySel::baseFromp(AstNode* nodep) { ///< What is the base variable (or const) this dereferences?
|
||||
// Else AstArraySel etc; search for the base
|
||||
while (nodep) {
|
||||
if (nodep->castArraySel()) { nodep=nodep->castArraySel()->fromp(); continue; }
|
||||
else if (nodep->castSel()) { nodep=nodep->castSel()->fromp(); continue; }
|
||||
if (VN_IS(nodep, ArraySel)) { nodep=VN_CAST(nodep, ArraySel)->fromp(); continue; }
|
||||
else if (VN_IS(nodep, Sel)) { nodep=VN_CAST(nodep, Sel)->fromp(); continue; }
|
||||
// AstNodeSelPre stashes the associated variable under an ATTROF of AstAttrType::VAR_BASE/MEMBER_BASE so it isn't constified
|
||||
else if (nodep->castAttrOf()) { nodep=nodep->castAttrOf()->fromp(); continue; }
|
||||
else if (nodep->castNodePreSel()) {
|
||||
if (nodep->castNodePreSel()->attrp()) {
|
||||
nodep=nodep->castNodePreSel()->attrp();
|
||||
else if (VN_IS(nodep, AttrOf)) { nodep=VN_CAST(nodep, AttrOf)->fromp(); continue; }
|
||||
else if (VN_IS(nodep, NodePreSel)) {
|
||||
if (VN_CAST(nodep, NodePreSel)->attrp()) {
|
||||
nodep=VN_CAST(nodep, NodePreSel)->attrp();
|
||||
} else {
|
||||
nodep=nodep->castNodePreSel()->lhsp();
|
||||
nodep=VN_CAST(nodep, NodePreSel)->lhsp();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -580,7 +594,7 @@ string AstScope::nameDotless() const {
|
|||
|
||||
string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
|
||||
string out;
|
||||
for (AstText* textp=scopeTextp; textp; textp=textp->nextp()->castText()) {
|
||||
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
|
||||
out += textp->text();
|
||||
}
|
||||
// TOP will be replaced by top->name()
|
||||
|
|
@ -592,7 +606,7 @@ string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
|
|||
|
||||
string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
||||
string out;
|
||||
for (AstText* textp=scopeTextp; textp; textp=textp->nextp()->castText()) {
|
||||
for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) {
|
||||
out += textp->text();
|
||||
}
|
||||
if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,"");
|
||||
|
|
@ -610,28 +624,28 @@ string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const {
|
|||
|
||||
bool AstSenTree::hasClocked() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isClocked()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasSettle() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isSettle()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasInitial() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isInitial()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasCombo() const {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
if (senp->isCombo()) return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -652,7 +666,7 @@ void AstTypeTable::clearCache() {
|
|||
m_detailedMap.clear();
|
||||
// Clear generic()'s so dead detection will work
|
||||
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
|
||||
if (AstBasicDType* bdtypep = nodep->castBasicDType()) {
|
||||
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
|
||||
bdtypep->generic(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -662,7 +676,7 @@ void AstTypeTable::repairCache() {
|
|||
// After we mass-change widthMin in V3WidthCommit, we need to correct the table.
|
||||
clearCache();
|
||||
for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) {
|
||||
if (AstBasicDType* bdtypep = nodep->castBasicDType()) {
|
||||
if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) {
|
||||
(void)findInsertSameDType(bdtypep);
|
||||
}
|
||||
}
|
||||
|
|
@ -689,7 +703,7 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw
|
|||
if (kwd == AstBasicDTypeKwd::LOGIC) idx = IDX0_LOGIC;
|
||||
else if (kwd == AstBasicDTypeKwd::BIT) idx = IDX0_BIT;
|
||||
else fl->v3fatalSrc("Bad kwd for findLogicBitDType");
|
||||
pair<int,int> widths = make_pair(width,widthMin);
|
||||
std::pair<int,int> widths = make_pair(width,widthMin);
|
||||
LogicMap& mapr = m_logicMap[idx][(int)numeric];
|
||||
LogicMap::const_iterator it = mapr.find(widths);
|
||||
if (it != mapr.end()) return it->second;
|
||||
|
|
@ -792,12 +806,12 @@ void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
|
|||
//======================================================================
|
||||
// Per-type Debugging
|
||||
|
||||
void AstNode::dump(ostream& str) {
|
||||
void AstNode::dump(std::ostream& str) {
|
||||
str<<typeName()<<" "<<(void*)this
|
||||
//<<" "<<(void*)this->m_backp
|
||||
<<" <e"<<dec<<editCount()
|
||||
<<" <e"<<std::dec<<editCount()
|
||||
<<((editCount()>=editCountLast())?"#>":">")
|
||||
<<" {"<<fileline()->filenameLetters()<<dec<<fileline()->lineno()<<"}";
|
||||
<<" {"<<fileline()->filenameLetters()<<std::dec<<fileline()->lineno()<<"}";
|
||||
if (user1p()) str<<" u1="<<(void*)user1p();
|
||||
if (user2p()) str<<" u2="<<(void*)user2p();
|
||||
if (user3p()) str<<" u3="<<(void*)user3p();
|
||||
|
|
@ -813,50 +827,50 @@ void AstNode::dump(ostream& str) {
|
|||
if (dtypep()) str<<" %Error-dtype-exp=null,got="<<(void*)dtypep();
|
||||
}
|
||||
if (name()!="") {
|
||||
if (castConst()) str<<" "<<name(); // Already quoted
|
||||
if (VN_IS(this, Const)) str<<" "<<name(); // Already quoted
|
||||
else str<<" "<<V3Number::quoteNameControls(name());
|
||||
}
|
||||
}
|
||||
|
||||
void AstAlways::dump(ostream& str) {
|
||||
void AstAlways::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (keyword() != VAlwaysKwd::ALWAYS) str<<" ["<<keyword().ascii()<<"]";
|
||||
}
|
||||
|
||||
void AstAttrOf::dump(ostream& str) {
|
||||
void AstAttrOf::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" ["<<attrType().ascii()<<"]";
|
||||
}
|
||||
void AstBasicDType::dump(ostream& str) {
|
||||
void AstBasicDType::dump(std::ostream& str) {
|
||||
this->AstNodeDType::dump(str);
|
||||
str<<" kwd="<<keyword().ascii();
|
||||
if (isRanged() && !rangep()) str<<" range=["<<left()<<":"<<right()<<"]";
|
||||
}
|
||||
void AstCCast::dump(ostream& str) {
|
||||
void AstCCast::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" sz"<<size();
|
||||
}
|
||||
void AstCell::dump(ostream& str) {
|
||||
void AstCell::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (recursive()) str<<" [RECURSIVE]";
|
||||
if (modp()) { str<<" -> "; modp()->dump(str); }
|
||||
else { str<<" ->UNLINKED:"<<modName(); }
|
||||
}
|
||||
void AstCellInline::dump(ostream& str) {
|
||||
void AstCellInline::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> "<<origModName();
|
||||
}
|
||||
void AstDisplay::dump(ostream& str) {
|
||||
void AstDisplay::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
//str<<" "<<displayType().ascii();
|
||||
}
|
||||
void AstEnumItemRef::dump(ostream& str) {
|
||||
void AstEnumItemRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (itemp()) { itemp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstIfaceRefDType::dump(ostream& str) {
|
||||
void AstIfaceRefDType::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (cellName()!="") { str<<" cell="<<cellName(); }
|
||||
if (ifaceName()!="") { str<<" if="<<ifaceName(); }
|
||||
|
|
@ -865,54 +879,54 @@ void AstIfaceRefDType::dump(ostream& str) {
|
|||
else if (ifacep()) { str<<" -> "; ifacep()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstIfaceRefDType::dumpSmall(ostream& str) {
|
||||
void AstIfaceRefDType::dumpSmall(std::ostream& str) {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"iface";
|
||||
}
|
||||
void AstJumpGo::dump(ostream& str) {
|
||||
void AstJumpGo::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (labelp()) { labelp()->dump(str); }
|
||||
else { str<<"%Error:UNLINKED"; }
|
||||
}
|
||||
void AstModportFTaskRef::dump(ostream& str) {
|
||||
void AstModportFTaskRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (isExport()) str<<" EXPORT";
|
||||
if (isImport()) str<<" IMPORT";
|
||||
if (ftaskp()) { str<<" -> "; ftaskp()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstModportVarRef::dump(ostream& str) {
|
||||
void AstModportVarRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" "<<varType();
|
||||
if (varp()) { str<<" -> "; varp()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstPin::dump(ostream& str) {
|
||||
void AstPin::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (modVarp()) { str<<" -> "; modVarp()->dump(str); }
|
||||
else { str<<" ->UNLINKED"; }
|
||||
if (svImplicit()) str<<" [.SV]";
|
||||
}
|
||||
void AstTypedef::dump(ostream& str) {
|
||||
void AstTypedef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (attrPublic()) str<<" [PUBLIC]";
|
||||
}
|
||||
void AstRange::dump(ostream& str) {
|
||||
void AstRange::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (littleEndian()) str<<" [LITTLE]";
|
||||
}
|
||||
void AstRefDType::dump(ostream& str) {
|
||||
void AstRefDType::dump(std::ostream& str) {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (defp()) { str<<" -> "; defp()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstNodeClassDType::dump(ostream& str) {
|
||||
void AstNodeClassDType::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (packed()) str<<" [PACKED]";
|
||||
if (isFourstate()) str<<" [4STATE]";
|
||||
}
|
||||
void AstNodeDType::dump(ostream& str) {
|
||||
void AstNodeDType::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (generic()) str<<" [GENERIC]";
|
||||
if (AstNodeDType* dtp = virtRefDTypep()) {
|
||||
|
|
@ -920,7 +934,7 @@ void AstNodeDType::dump(ostream& str) {
|
|||
dtp->dumpSmall(str);
|
||||
}
|
||||
}
|
||||
void AstNodeDType::dumpSmall(ostream& str) {
|
||||
void AstNodeDType::dumpSmall(std::ostream& str) {
|
||||
str<<"("
|
||||
<<(generic()?"G/":"")
|
||||
<<((isSigned()&&!isDouble())?"s":"")
|
||||
|
|
@ -933,16 +947,16 @@ void AstNodeDType::dumpSmall(ostream& str) {
|
|||
if (!widthSized()) str<<"/"<<widthMin();
|
||||
str<<")";
|
||||
}
|
||||
void AstNodeArrayDType::dumpSmall(ostream& str) {
|
||||
void AstNodeArrayDType::dumpSmall(std::ostream& str) {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
if (castPackArrayDType()) str<<"p"; else str<<"u";
|
||||
if (VN_IS(this, PackArrayDType)) str<<"p"; else str<<"u";
|
||||
str<<declRange();
|
||||
}
|
||||
void AstNodeArrayDType::dump(ostream& str) {
|
||||
void AstNodeArrayDType::dump(std::ostream& str) {
|
||||
this->AstNodeDType::dump(str);
|
||||
str<<" "<<declRange();
|
||||
}
|
||||
void AstNodeModule::dump(ostream& str) {
|
||||
void AstNodeModule::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" L"<<level();
|
||||
if (modPublic()) str<<" [P]";
|
||||
|
|
@ -951,33 +965,38 @@ void AstNodeModule::dump(ostream& str) {
|
|||
if (recursiveClone()) str<<" [RECURSIVE-CLONE]";
|
||||
else if (recursive()) str<<" [RECURSIVE]";
|
||||
}
|
||||
void AstPackageExport::dump(ostream& str) {
|
||||
void AstPackageExport::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> "<<packagep();
|
||||
}
|
||||
void AstPackageImport::dump(ostream& str) {
|
||||
void AstPackageImport::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> "<<packagep();
|
||||
}
|
||||
void AstSel::dump(ostream& str) {
|
||||
void AstSel::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (declRange().ranged()) {
|
||||
str<<" decl"<<declRange()<<"]";
|
||||
if (declElWidth()!=1) str<<"/"<<declElWidth();
|
||||
}
|
||||
}
|
||||
void AstSliceSel::dump(ostream& str) {
|
||||
void AstSliceSel::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (declRange().ranged()) {
|
||||
str<<" decl"<<declRange();
|
||||
}
|
||||
}
|
||||
void AstTypeTable::dump(ostream& str) {
|
||||
void AstMTaskBody::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" ";
|
||||
m_execMTaskp->dump(str);
|
||||
}
|
||||
void AstTypeTable::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
for (int i=0; i<(int)(AstBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
if (AstBasicDType* subnodep=m_basicps[i]) {
|
||||
str<<endl; // Newline from caller, so newline first
|
||||
str<<"\t\t"<<setw(8)<<AstBasicDTypeKwd(i).ascii();
|
||||
str<<"\t\t"<<std::setw(8)<<AstBasicDTypeKwd(i).ascii();
|
||||
str<<" -> ";
|
||||
subnodep->dump(str);
|
||||
}
|
||||
|
|
@ -988,10 +1007,10 @@ void AstTypeTable::dump(ostream& str) {
|
|||
for (LogicMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) {
|
||||
AstBasicDType* dtypep = it->second;
|
||||
str<<endl; // Newline from caller, so newline first
|
||||
stringstream nsstr;
|
||||
std::stringstream nsstr;
|
||||
nsstr<<(isbit?"bw":"lw")
|
||||
<<it->first.first<<"/"<<it->first.second;
|
||||
str<<"\t\t"<<setw(8)<<nsstr.str();
|
||||
str<<"\t\t"<<std::setw(8)<<nsstr.str();
|
||||
if (issigned) str<<" s"; else str<<" u";
|
||||
str<<" -> ";
|
||||
dtypep->dump(str);
|
||||
|
|
@ -1009,17 +1028,17 @@ void AstTypeTable::dump(ostream& str) {
|
|||
}
|
||||
// Note get newline from caller too.
|
||||
}
|
||||
void AstUnsizedArrayDType::dumpSmall(ostream& str) {
|
||||
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"[]";
|
||||
}
|
||||
void AstVarScope::dump(ostream& str) {
|
||||
void AstVarScope::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (isCircular()) str<<" [CIRC]";
|
||||
if (varp()) { str<<" -> "; varp()->dump(str); }
|
||||
else { str<<" ->UNLINKED"; }
|
||||
}
|
||||
void AstVarXRef::dump(ostream& str) {
|
||||
void AstVarXRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
|
||||
if (lvalue()) str<<" [LV] => ";
|
||||
|
|
@ -1030,7 +1049,7 @@ void AstVarXRef::dump(ostream& str) {
|
|||
else if (varp()) { varp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstVarRef::dump(ostream& str) {
|
||||
void AstVarRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
|
||||
if (lvalue()) str<<" [LV] => ";
|
||||
|
|
@ -1039,7 +1058,7 @@ void AstVarRef::dump(ostream& str) {
|
|||
else if (varp()) { varp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstVar::dump(ostream& str) {
|
||||
void AstVar::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (isSc()) str<<" [SC]";
|
||||
if (isPrimaryIO()) str<<(isInout()?" [PIO]":(isInput()?" [PI]":" [PO]"));
|
||||
|
|
@ -1063,35 +1082,35 @@ void AstVar::dump(ostream& str) {
|
|||
if (!attrClocker().unknown()) str<<" ["<<attrClocker().ascii()<<"] ";
|
||||
str<<" "<<varType();
|
||||
}
|
||||
void AstSenTree::dump(ostream& str) {
|
||||
void AstSenTree::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (isMulti()) str<<" [MULTI]";
|
||||
}
|
||||
void AstSenItem::dump(ostream& str) {
|
||||
void AstSenItem::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" ["<<edgeType().ascii()<<"]";
|
||||
}
|
||||
void AstParseRef::dump(ostream& str) {
|
||||
void AstParseRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" ["<<expect().ascii()<<"]";
|
||||
}
|
||||
void AstPackageRef::dump(ostream& str) {
|
||||
void AstPackageRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
|
||||
str<<" -> ";
|
||||
if (packagep()) { packagep()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstDot::dump(ostream& str) {
|
||||
void AstDot::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
}
|
||||
void AstActive::dump(ostream& str) {
|
||||
void AstActive::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" => ";
|
||||
if (sensesp()) { sensesp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstNodeFTaskRef::dump(ostream& str) {
|
||||
void AstNodeFTaskRef::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (packagep()) { str<<" pkg="<<(void*)packagep(); }
|
||||
str<<" -> ";
|
||||
|
|
@ -1099,7 +1118,7 @@ void AstNodeFTaskRef::dump(ostream& str) {
|
|||
if (taskp()) { taskp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstNodeFTask::dump(ostream& str) {
|
||||
void AstNodeFTask::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (taskPublic()) str<<" [PUBLIC]";
|
||||
if (prototype()) str<<" [PROTOTYPE]";
|
||||
|
|
@ -1109,34 +1128,34 @@ void AstNodeFTask::dump(ostream& str) {
|
|||
if (dpiOpenParent()) str<<" [DPIOPENPARENT]";
|
||||
if ((dpiImport() || dpiExport()) && cname()!=name()) str<<" [c="<<cname()<<"]";
|
||||
}
|
||||
void AstBegin::dump(ostream& str) {
|
||||
void AstBegin::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (unnamed()) str<<" [UNNAMED]";
|
||||
if (generate()) str<<" [GEN]";
|
||||
if (genforp()) str<<" [GENFOR]";
|
||||
}
|
||||
void AstCoverDecl::dump(ostream& str) {
|
||||
void AstCoverDecl::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (this->dataDeclNullp()) {
|
||||
str<<" -> ";
|
||||
this->dataDeclNullp()->dump(str);
|
||||
} else {
|
||||
if (binNum()) { str<<" bin"<<dec<<binNum(); }
|
||||
if (binNum()) { str<<" bin"<<std::dec<<binNum(); }
|
||||
}
|
||||
}
|
||||
void AstCoverInc::dump(ostream& str) {
|
||||
void AstCoverInc::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (declp()) { declp()->dump(str); }
|
||||
else { str<<"%Error:UNLINKED"; }
|
||||
}
|
||||
void AstTraceInc::dump(ostream& str) {
|
||||
void AstTraceInc::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (declp()) { declp()->dump(str); }
|
||||
else { str<<"%Error:UNLINKED"; }
|
||||
}
|
||||
void AstNodeText::dump(ostream& str) {
|
||||
void AstNodeText::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
string out = text();
|
||||
string::size_type pos;
|
||||
|
|
@ -1147,19 +1166,19 @@ void AstNodeText::dump(ostream& str) {
|
|||
str<<" \""<<out<<"\"";
|
||||
}
|
||||
|
||||
void AstCFile::dump(ostream& str) {
|
||||
void AstCFile::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (source()) str<<" [SRC]";
|
||||
if (slow()) str<<" [SLOW]";
|
||||
}
|
||||
void AstCCall::dump(ostream& str) {
|
||||
void AstCCall::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (funcp()) {
|
||||
str<<" "<<funcp()->name()<<" => ";
|
||||
funcp()->dump(str);
|
||||
}
|
||||
}
|
||||
void AstCFunc::dump(ostream& str) {
|
||||
void AstCFunc::dump(std::ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (slow()) str<<" [SLOW]";
|
||||
if (pure()) str<<" [PURE]";
|
||||
|
|
|
|||
303
src/V3AstNodes.h
303
src/V3AstNodes.h
|
|
@ -28,14 +28,19 @@
|
|||
//######################################################################
|
||||
// Standard defines for all AstNode final classes
|
||||
|
||||
#define ASTNODE_NODE_FUNCS(name) \
|
||||
virtual ~Ast ##name() {} \
|
||||
#define ASTNODE_NODE_FUNCS_NO_DTOR(name) \
|
||||
virtual void accept(AstNVisitor& v) { v.visit(this); } \
|
||||
virtual AstType type() const { return AstType::at ## name; } \
|
||||
virtual AstNode* clone() { return new Ast ##name (*this); } \
|
||||
virtual void accept(AstNVisitor& v) { v.visit(this); } \
|
||||
static Ast ##name * cloneTreeNull(Ast ##name * nodep, bool cloneNextLink) { \
|
||||
return nodep ? nodep->cloneTree(cloneNextLink) : NULL; } \
|
||||
Ast ##name * cloneTree(bool cloneNext) { return static_cast<Ast ##name *>(AstNode::cloneTree(cloneNext)); } \
|
||||
Ast ##name * clonep() const { return static_cast<Ast ##name *>(AstNode::clonep()); }
|
||||
|
||||
#define ASTNODE_NODE_FUNCS(name) \
|
||||
virtual ~Ast ##name() {} \
|
||||
ASTNODE_NODE_FUNCS_NO_DTOR(name)
|
||||
|
||||
//######################################################################
|
||||
//=== Ast* : Specific types
|
||||
// Netlist interconnect
|
||||
|
|
@ -47,26 +52,26 @@ private:
|
|||
public:
|
||||
AstConst(FileLine* fl, const V3Number& num)
|
||||
:AstNodeMath(fl)
|
||||
,m_num(num) {
|
||||
,m_num(num) {
|
||||
if (m_num.isDouble()) {
|
||||
dtypeSetDouble();
|
||||
} else if (m_num.isString()) {
|
||||
} else if (m_num.isString()) {
|
||||
dtypeSetString();
|
||||
} else {
|
||||
dtypeSetLogicSized(m_num.width(), m_num.sized()?0:m_num.widthMin(),
|
||||
m_num.isSigned() ? AstNumeric::SIGNED
|
||||
dtypeSetLogicSized(m_num.width(), m_num.sized()?0:m_num.widthMin(),
|
||||
m_num.isSigned() ? AstNumeric::SIGNED
|
||||
: AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
}
|
||||
AstConst(FileLine* fl, uint32_t num)
|
||||
:AstNodeMath(fl)
|
||||
,m_num(V3Number(fl,32,num)) { dtypeSetLogicSized(m_num.width(),
|
||||
,m_num(V3Number(fl,32,num)) { dtypeSetLogicSized(m_num.width(),
|
||||
m_num.sized()?0:m_num.widthMin(),
|
||||
AstNumeric::UNSIGNED); }
|
||||
class Unsized32 {}; // for creator type-overload selection
|
||||
class Unsized32 {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, Unsized32, uint32_t num) // Unsized 32-bit integer of specified value
|
||||
:AstNodeMath(fl)
|
||||
,m_num(V3Number(fl,32,num)) { m_num.width(32,false); dtypeSetLogicSized(32,m_num.widthMin(),
|
||||
:AstNodeMath(fl)
|
||||
,m_num(V3Number(fl,32,num)) { m_num.width(32,false); dtypeSetLogicSized(32,m_num.widthMin(),
|
||||
AstNumeric::UNSIGNED); }
|
||||
class Signed32 {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, Signed32, int32_t num) // Signed 32-bit integer of specified value
|
||||
|
|
@ -79,7 +84,7 @@ public:
|
|||
,m_num(V3Number(fl,64)) { m_num.setDouble(num); dtypeSetDouble(); }
|
||||
class String {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, String, const string& num)
|
||||
:AstNodeMath(fl)
|
||||
:AstNodeMath(fl)
|
||||
,m_num(V3Number(V3Number::String(), fl, num)) { dtypeSetString(); }
|
||||
class LogicFalse {};
|
||||
AstConst(FileLine* fl, LogicFalse) // Shorthand const 0, know the dtype should be a logic of size 1
|
||||
|
|
@ -132,15 +137,15 @@ public:
|
|||
AstNode* lsbp() const { return op3p(); } // op3 = Lsb expression
|
||||
AstNode* leftp() const { return littleEndian()?lsbp():msbp(); } // How to show a declaration
|
||||
AstNode* rightp() const { return littleEndian()?msbp():lsbp(); }
|
||||
int msbConst() const { AstConst* constp=msbp()->castConst(); return (constp?constp->toSInt():0); }
|
||||
int lsbConst() const { AstConst* constp=lsbp()->castConst(); return (constp?constp->toSInt():0); }
|
||||
int msbConst() const { AstConst* constp=VN_CAST(msbp(), Const); return (constp?constp->toSInt():0); }
|
||||
int lsbConst() const { AstConst* constp=VN_CAST(lsbp(), Const); return (constp?constp->toSInt():0); }
|
||||
int elementsConst() const { return (msbConst()>lsbConst()) ? msbConst()-lsbConst()+1 : lsbConst()-msbConst()+1; }
|
||||
int leftConst() const { AstConst* constp=leftp()->castConst(); return (constp?constp->toSInt():0); }
|
||||
int rightConst() const { AstConst* constp=rightp()->castConst(); return (constp?constp->toSInt():0); }
|
||||
int leftConst() const { AstConst* constp=VN_CAST(leftp(), Const); return (constp?constp->toSInt():0); }
|
||||
int rightConst() const { AstConst* constp=VN_CAST(rightp(), Const); return (constp?constp->toSInt():0); }
|
||||
int leftToRightInc() const { return littleEndian()?1:-1; }
|
||||
bool littleEndian() const { return m_littleEndian; }
|
||||
void littleEndian(bool flag) { m_littleEndian=flag; }
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
|
|
@ -168,7 +173,7 @@ public:
|
|||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
virtual bool cleanOut() { return true; }
|
||||
AstNode* exprp() const { return op1p(); } // op1 = Pin expression
|
||||
AstRange* rangep() const { return op2p()->castRange(); } // op2 = Range of pin
|
||||
AstRange* rangep() const { return VN_CAST(op2p(), Range); } // op2 = Range of pin
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -188,7 +193,7 @@ public:
|
|||
}
|
||||
ASTNODE_NODE_FUNCS(ParamTypeDType)
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Type assigning to
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type
|
||||
|
|
@ -196,7 +201,7 @@ public:
|
|||
virtual AstNodeDType* skipRefToConstp() const { return subDTypep()->skipRefToConstp(); }
|
||||
virtual AstNodeDType* skipRefToEnump() const { return subDTypep()->skipRefToEnump(); }
|
||||
virtual bool similarDType(AstNodeDType* samep) const {
|
||||
AstParamTypeDType* sp = samep->castParamTypeDType();
|
||||
const AstParamTypeDType* sp = static_cast<const AstParamTypeDType*>(samep);
|
||||
return (sp && this->subDTypep()->skipRefp()->similarDType(sp->subDTypep()->skipRefp()));
|
||||
}
|
||||
virtual int widthAlignBytes() const { return dtypep()->widthAlignBytes(); }
|
||||
|
|
@ -225,9 +230,9 @@ public:
|
|||
m_attrPublic = false;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Typedef)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Type assigning to
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
void addAttrsp(AstNode* nodep) { addNOp4p(nodep); }
|
||||
|
|
@ -279,7 +284,7 @@ public:
|
|||
return type()==samep->type() && same(samep); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(m_uniqueNum); }
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
void* containerp() const { return m_containerp; }
|
||||
|
|
@ -370,10 +375,10 @@ public:
|
|||
const AstNodeArrayDType* asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return (subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
|
||||
}
|
||||
virtual void dumpSmall(ostream& str);
|
||||
virtual void dumpSmall(std::ostream& str);
|
||||
virtual V3Hash sameHash() const { return V3Hash(m_refDTypep); }
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
|
|
@ -457,7 +462,7 @@ private:
|
|||
}
|
||||
public:
|
||||
ASTNODE_NODE_FUNCS(BasicDType)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m.m_keyword), V3Hash(m.m_nrange.hi())); }
|
||||
virtual bool same(const AstNode* samep) const { // width/widthMin/numeric compared elsewhere
|
||||
const AstBasicDType* sp = static_cast<const AstBasicDType*>(samep);
|
||||
|
|
@ -466,7 +471,7 @@ public:
|
|||
return type()==samep->type() && same(samep); }
|
||||
virtual string name() const { return m.m_keyword.ascii(); }
|
||||
virtual const char* broken() const { BROKEN_RTN(dtypep()!=this); return NULL; }
|
||||
AstRange* rangep() const { return op1p()->castRange(); } // op1 = Range of variable
|
||||
AstRange* rangep() const { return VN_CAST(op1p(), Range); } // op1 = Range of variable
|
||||
void rangep(AstRange* nodep) { setNOp1p(nodep); }
|
||||
void setSignedState(VSignedState signst) {
|
||||
// Note NOSIGN does NOT change the state; this is required by the parser
|
||||
|
|
@ -498,7 +503,7 @@ public:
|
|||
bool implicit() const { return keyword() == AstBasicDTypeKwd::LOGIC_IMPLICIT; }
|
||||
VNumRange declRange() const { return isRanged() ? VNumRange(msb(), lsb(), littleEndian()) : VNumRange(); }
|
||||
void cvtRangeConst() { // Convert to smaller represenation
|
||||
if (rangep() && rangep()->msbp()->castConst() && rangep()->lsbp()->castConst()) {
|
||||
if (rangep() && VN_IS(rangep()->msbp(), Const) && VN_IS(rangep()->lsbp(), Const)) {
|
||||
m.m_nrange.init(rangep()->msbConst(), rangep()->lsbConst(),
|
||||
rangep()->littleEndian());
|
||||
rangep()->unlinkFrBackWithNext()->deleteTree();
|
||||
|
|
@ -534,7 +539,7 @@ public:
|
|||
return skipRefp()->similarDType(samep->skipRefp()); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(m_refDTypep); } // node's type() included elsewhere
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
|
|
@ -568,8 +573,8 @@ public:
|
|||
ASTNODE_NODE_FUNCS(IfaceRefDType)
|
||||
// METHODS
|
||||
virtual const char* broken() const;
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dumpSmall(ostream& str);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
virtual void dumpSmall(std::ostream& str);
|
||||
virtual void cloneRelink();
|
||||
virtual AstBasicDType* basicp() const { return NULL; }
|
||||
virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; }
|
||||
|
|
@ -621,7 +626,7 @@ public:
|
|||
virtual bool similarDType(AstNodeDType* samep) const {
|
||||
return skipRefp()->similarDType(samep->skipRefp()); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep),V3Hash(m_packagep)); }
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
virtual string name() const { return m_name; }
|
||||
virtual AstBasicDType* basicp() const { return subDTypep() ? subDTypep()->basicp() : NULL; }
|
||||
virtual AstNodeDType* skipRefp() const {
|
||||
|
|
@ -698,7 +703,7 @@ public:
|
|||
virtual bool hasDType() const { return true; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
|
|
@ -738,7 +743,7 @@ public:
|
|||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual bool hasDType() const { return true; }
|
||||
void name(const string& flag) { m_name = flag; }
|
||||
AstRange* rangep() const { return op1p()->castRange(); } // op1 = Range for name appending
|
||||
AstRange* rangep() const { return VN_CAST(op1p(), Range); } // op1 = Range for name appending
|
||||
void rangep(AstNode* nodep) { addOp1p(nodep); }
|
||||
AstNode* valuep() const { return op2p(); } // op2 = Value
|
||||
void valuep(AstNode* nodep) { addOp2p(nodep); }
|
||||
|
|
@ -754,11 +759,11 @@ public:
|
|||
dtypeFrom(m_itemp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(EnumItemRef)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual string name() const { return itemp()->name(); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!itemp()); return NULL; }
|
||||
virtual int instrCount() const { return 0; }
|
||||
virtual void cloneRelink() { if (m_itemp->clonep()) m_itemp = m_itemp->clonep()->castEnumItem(); }
|
||||
virtual void cloneRelink() { if (m_itemp->clonep()) m_itemp = VN_CAST(m_itemp->clonep(), EnumItem); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstEnumItemRef* sp = static_cast<const AstEnumItemRef*>(samep);
|
||||
return itemp() == sp->itemp(); }
|
||||
|
|
@ -798,13 +803,13 @@ public:
|
|||
virtual bool similarDType(AstNodeDType* samep) const { return this==samep; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(m_uniqueNum); }
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Data type
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Data type
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; }
|
||||
virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); }
|
||||
AstEnumItem* itemsp() const { return op2p()->castEnumItem(); } // op2 = AstEnumItem's
|
||||
AstEnumItem* itemsp() const { return VN_CAST(op2p(), EnumItem); } // op2 = AstEnumItem's
|
||||
void addValuesp(AstNode* nodep) { addOp2p(nodep); }
|
||||
// METHODS
|
||||
virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type
|
||||
|
|
@ -841,9 +846,9 @@ class AstArraySel : public AstNodeSel {
|
|||
// Children: varref|arraysel, math
|
||||
private:
|
||||
void init(AstNode* fromp) {
|
||||
if (fromp && fromp->dtypep()->skipRefp()->castNodeArrayDType()) {
|
||||
if (fromp && VN_IS(fromp->dtypep()->skipRefp(), NodeArrayDType)) {
|
||||
// Strip off array to find what array references
|
||||
dtypeFrom(fromp->dtypep()->skipRefp()->castNodeArrayDType()->subDTypep());
|
||||
dtypeFrom(VN_CAST(fromp->dtypep()->skipRefp(), NodeArrayDType)->subDTypep());
|
||||
}
|
||||
}
|
||||
public:
|
||||
|
|
@ -949,9 +954,9 @@ public:
|
|||
AstSel(FileLine* fl, AstNode* fromp, AstNode* lsbp, AstNode* widthp)
|
||||
:AstNodeTriop(fl, fromp, lsbp, widthp) {
|
||||
m_declElWidth = 1;
|
||||
if (widthp->castConst()) {
|
||||
dtypeSetLogicSized(widthp->castConst()->toUInt(),
|
||||
widthp->castConst()->toUInt(),
|
||||
if (VN_IS(widthp, Const)) {
|
||||
dtypeSetLogicSized(VN_CAST(widthp, Const)->toUInt(),
|
||||
VN_CAST(widthp, Const)->toUInt(),
|
||||
AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
|
|
@ -962,7 +967,7 @@ public:
|
|||
dtypeSetLogicSized(bitwidth,bitwidth,AstNumeric::UNSIGNED);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Sel)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual void numberOperate(V3Number& out, const V3Number& from, const V3Number& bit, const V3Number& width) {
|
||||
out.opSel(from, bit.toUInt()+width.toUInt()-1, bit.toUInt()); }
|
||||
virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially
|
||||
|
|
@ -977,13 +982,13 @@ public:
|
|||
virtual bool sizeMattersThs() {return false;}
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
virtual int instrCount() const { return widthInstrs()*(lsbp()->castConst()?3:10); }
|
||||
virtual int instrCount() const { return widthInstrs()*(VN_CAST(lsbp(), Const)?3:10); }
|
||||
AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing)
|
||||
AstNode* lsbp() const { return op2p(); } // op2 = Msb selection expression
|
||||
AstNode* widthp() const { return op3p(); } // op3 = Width
|
||||
int widthConst() const { return widthp()->castConst()->toSInt(); }
|
||||
int lsbConst() const { return lsbp()->castConst()->toSInt(); }
|
||||
int msbConst() const { return lsbConst()+widthConst()-1; }
|
||||
int widthConst() const { return VN_CAST(widthp(), Const)->toSInt(); }
|
||||
int lsbConst() const { return VN_CAST(lsbp(), Const)->toSInt(); }
|
||||
int msbConst() const { return lsbConst()+widthConst()-1; }
|
||||
VNumRange& declRange() { return m_declRange; }
|
||||
void declRange(const VNumRange& flag) { m_declRange = flag; }
|
||||
int declElWidth() const { return m_declElWidth; }
|
||||
|
|
@ -1003,7 +1008,7 @@ public:
|
|||
new AstConst(fl, declRange.elements()))
|
||||
, m_declRange(declRange) { }
|
||||
ASTNODE_NODE_FUNCS(SliceSel)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual void numberOperate(V3Number& out, const V3Number& from, const V3Number& lo, const V3Number& width) {
|
||||
V3ERROR_NA; }
|
||||
virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially
|
||||
|
|
@ -1119,6 +1124,7 @@ private:
|
|||
bool m_noSubst:1; // Do not substitute out references
|
||||
bool m_trace:1; // Trace this variable
|
||||
AstVarAttrClocker m_attrClocker;
|
||||
MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var
|
||||
|
||||
void init() {
|
||||
m_input=false; m_output=false; m_tristate=false; m_declOutput=false;
|
||||
|
|
@ -1174,7 +1180,7 @@ public:
|
|||
dtypeFrom(examplep);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Var)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual bool hasDType() const { return true; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
|
|
@ -1194,7 +1200,7 @@ public:
|
|||
string vlPropInit() const; // Return VerilatorVarProps initializer
|
||||
void combineType(AstVarType type);
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
|
||||
AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); }
|
||||
// (Slow) recurse down to find basic data type (Note don't need virtual - AstVar isn't a NodeDType)
|
||||
AstBasicDType* basicp() const { return subDTypep()->basicp(); }
|
||||
|
|
@ -1318,6 +1324,10 @@ public:
|
|||
if (varType()==AstVarType::INPUT || varType()==AstVarType::OUTPUT) m_varType = AstVarType::WIRE;
|
||||
}
|
||||
static AstVar* scVarRecurse(AstNode* nodep);
|
||||
void addProducingMTaskId(int id) { m_mtaskIds.insert(id); }
|
||||
void addConsumingMTaskId(int id) { m_mtaskIds.insert(id); }
|
||||
const MTaskIdSet& mtaskIds() const { return m_mtaskIds; }
|
||||
string mtasksString() const;
|
||||
};
|
||||
|
||||
class AstDefParam : public AstNode {
|
||||
|
|
@ -1402,7 +1412,7 @@ public:
|
|||
ASTNODE_NODE_FUNCS(TopScope)
|
||||
AstNode* stmtsp() const { return op1p(); }
|
||||
void addStmtsp(AstNode* nodep) { addOp1p(nodep); }
|
||||
AstScope* scopep() const { return op2p()->castScope(); } // op1 = AstVarScope's
|
||||
AstScope* scopep() const { return VN_CAST(op2p(), Scope); } // op1 = AstVarScope's
|
||||
};
|
||||
|
||||
class AstVarScope : public AstNode {
|
||||
|
|
@ -1433,7 +1443,7 @@ public:
|
|||
BROKEN_RTN(m_scopep && !m_scopep->brokeExists()); return NULL; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual string name() const {return scopep()->name()+"->"+varp()->name();} // * = Var name
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual bool hasDType() const { return true; }
|
||||
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
|
||||
AstScope* scopep() const { return m_scopep; } // Pointer to scope it's under
|
||||
|
|
@ -1460,7 +1470,7 @@ public:
|
|||
varScopep(varscp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(VarRef)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash(varp()->name()),V3Hash(hiername())); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return same(static_cast<const AstVarRef*>(samep)); }
|
||||
|
|
@ -1496,7 +1506,7 @@ public:
|
|||
dtypeFrom(varp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(VarXRef)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
string dotted() const { return m_dotted; }
|
||||
void dotted(const string& dotted) { m_dotted = dotted; }
|
||||
string prettyDotted() const { return prettyName(dotted()); }
|
||||
|
|
@ -1542,7 +1552,7 @@ public:
|
|||
setNOp1p(exprp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Pin)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual const char* broken() const {
|
||||
BROKEN_RTN(m_modVarp && !m_modVarp->brokeExists());
|
||||
BROKEN_RTN(m_modPTypep && !m_modPTypep->brokeExists());
|
||||
|
|
@ -1643,7 +1653,7 @@ public:
|
|||
ASTNODE_NODE_FUNCS(PackageExport)
|
||||
virtual const char* broken() const { BROKEN_RTN(!m_packagep || !m_packagep->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_packagep && m_packagep->clonep()) m_packagep = m_packagep->clonep(); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual string name() const { return m_name; }
|
||||
AstPackage* packagep() const { return m_packagep; }
|
||||
void packagep(AstPackage* nodep) { m_packagep=nodep; }
|
||||
|
|
@ -1660,7 +1670,7 @@ public:
|
|||
ASTNODE_NODE_FUNCS(PackageImport)
|
||||
virtual const char* broken() const { BROKEN_RTN(!m_packagep || !m_packagep->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_packagep && m_packagep->clonep()) m_packagep = m_packagep->clonep(); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual string name() const { return m_name; }
|
||||
AstPackage* packagep() const { return m_packagep; }
|
||||
void packagep(AstPackage* nodep) { m_packagep=nodep; }
|
||||
|
|
@ -1687,7 +1697,7 @@ public:
|
|||
: AstNode(fl), m_name(name), m_export(isExport), m_ftaskp(NULL) { }
|
||||
ASTNODE_NODE_FUNCS(ModportFTaskRef)
|
||||
virtual const char* broken() const { BROKEN_RTN(m_ftaskp && !m_ftaskp->brokeExists()); return NULL; }
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual string name() const { return m_name; }
|
||||
virtual void cloneRelink() { if (m_ftaskp && m_ftaskp->clonep()) m_ftaskp = m_ftaskp->clonep(); }
|
||||
bool isImport() const { return !m_export; }
|
||||
|
|
@ -1709,7 +1719,7 @@ public:
|
|||
: AstNode(fl), m_name(name), m_type(type), m_varp(NULL) { }
|
||||
ASTNODE_NODE_FUNCS(ModportVarRef)
|
||||
virtual const char* broken() const { BROKEN_RTN(m_varp && !m_varp->brokeExists()); return NULL; }
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual void cloneRelink() { if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep(); }
|
||||
virtual string name() const { return m_name; }
|
||||
AstVarType varType() const { return m_type; } // * = Type of variable
|
||||
|
|
@ -1752,7 +1762,7 @@ public:
|
|||
addNOp1p(pinsp); addNOp2p(paramsp); setNOp3p(rangep); }
|
||||
ASTNODE_NODE_FUNCS(Cell)
|
||||
// No cloneRelink, we presume cloneee's want the same module linkages
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual const char* broken() const { BROKEN_RTN(m_modp && !m_modp->brokeExists()); return NULL; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
// ACCESSORS
|
||||
|
|
@ -1762,9 +1772,9 @@ public:
|
|||
void origName(const string& name) { m_origName = name; }
|
||||
string modName() const { return m_modName; } // * = Instance name
|
||||
void modName(const string& name) { m_modName = name; }
|
||||
AstPin* pinsp() const { return op1p()->castPin(); } // op1 = List of cell ports
|
||||
AstPin* paramsp() const { return op2p()->castPin(); } // op2 = List of parameter #(##) values
|
||||
AstRange* rangep() const { return op3p()->castRange(); } // op3 = Range of arrayed instants (NULL=not ranged)
|
||||
AstPin* pinsp() const { return VN_CAST(op1p(), Pin); } // op1 = List of cell ports
|
||||
AstPin* paramsp() const { return VN_CAST(op2p(), Pin); } // op2 = List of parameter #(##) values
|
||||
AstRange* rangep() const { return VN_CAST(op3p(), Range); } // op3 = Range of arrayed instants (NULL=not ranged)
|
||||
AstNodeModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated
|
||||
void addPinsp(AstPin* nodep) { addOp1p(nodep); }
|
||||
void addParamsp(AstPin* nodep) { addOp2p(nodep); }
|
||||
|
|
@ -1789,7 +1799,7 @@ public:
|
|||
: AstNode(fl)
|
||||
, m_name(name), m_origModName(origModName) {}
|
||||
ASTNODE_NODE_FUNCS(CellInline)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
// ACCESSORS
|
||||
virtual string name() const { return m_name; } // * = Cell name
|
||||
string origModName() const { return m_origModName; } // * = modp()->origName() before inlining
|
||||
|
|
@ -1855,7 +1865,7 @@ public:
|
|||
AstBind(FileLine* fl, const string& name, AstNode* cellsp)
|
||||
: AstNode(fl)
|
||||
, m_name(name) {
|
||||
if (!cellsp->castCell()) cellsp->v3fatalSrc("Only cells allowed to be bound");
|
||||
if (!VN_IS(cellsp, Cell)) cellsp->v3fatalSrc("Only cells allowed to be bound");
|
||||
addNOp1p(cellsp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Bind)
|
||||
|
|
@ -1910,7 +1920,7 @@ public:
|
|||
AstParseRef(FileLine* fl, AstParseRefExp expect, const string& name, AstNode* lhsp, AstNodeFTaskRef* ftaskrefp)
|
||||
:AstNode(fl), m_expect(expect), m_name(name) { setNOp1p(lhsp); setNOp2p(ftaskrefp); }
|
||||
ASTNODE_NODE_FUNCS(ParseRef)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_expect),V3Hash(m_name)); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
|
|
@ -1943,7 +1953,7 @@ public:
|
|||
virtual bool same(const AstNode* samep) const {
|
||||
return (m_packagep==static_cast<const AstPackageRef*>(samep)->m_packagep); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(m_packagep); }
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
AstPackage* packagep() const { return m_packagep; }
|
||||
void packagep(AstPackage* nodep) { m_packagep=nodep; }
|
||||
};
|
||||
|
|
@ -1959,7 +1969,7 @@ public:
|
|||
if (!packagep) return rhsp;
|
||||
return new AstDot(fl, new AstPackageRef(fl, packagep), rhsp);
|
||||
}
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual string emitVerilog() { V3ERROR_NA; return ""; }
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
|
|
@ -2063,14 +2073,14 @@ public:
|
|||
m_edgeType = AstEdgeType::ET_NEVER;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(SenItem)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual V3Hash sameHash() const { return V3Hash(edgeType()); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return edgeType()==static_cast<const AstSenItem*>(samep)->edgeType(); }
|
||||
AstEdgeType edgeType() const { return m_edgeType; } // * = Posedge/negedge
|
||||
void edgeType(AstEdgeType type) { m_edgeType=type; editCountInc(); }// * = Posedge/negedge
|
||||
AstNode* sensp() const { return op1p(); } // op1 = Signal sensitized
|
||||
AstNodeVarRef* varrefp() const { return op1p()->castNodeVarRef(); } // op1 = Signal sensitized
|
||||
AstNodeVarRef* varrefp() const { return VN_CAST(op1p(), NodeVarRef); } // op1 = Signal sensitized
|
||||
//
|
||||
virtual bool isClocked() const { return edgeType().clockedStmt(); }
|
||||
virtual bool isCombo() const { return edgeType()==AstEdgeType::ET_COMBO; }
|
||||
|
|
@ -2092,7 +2102,7 @@ public:
|
|||
}
|
||||
ASTNODE_NODE_FUNCS(SenGate)
|
||||
virtual string emitVerilog() { return "(%l) %f&& (%r)"; }
|
||||
AstSenItem* sensesp() const { return op1p()->castSenItem(); }
|
||||
AstSenItem* sensesp() const { return VN_CAST(op1p(), SenItem); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
void sensesp(AstSenItem* nodep) { addOp1p(nodep); }
|
||||
void rhsp(AstNode* nodep) { setOp2p(nodep); }
|
||||
|
|
@ -2116,10 +2126,11 @@ public:
|
|||
addNOp1p(sensesp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(SenTree)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
bool isMulti() const { return m_multi; }
|
||||
AstNodeSenItem* sensesp() const { return op1p()->castNodeSenItem(); } // op1 = Sensitivity list
|
||||
AstNodeSenItem* sensesp() const { return VN_CAST(op1p(), NodeSenItem); } // op1 = Sensitivity list
|
||||
void addSensesp(AstNodeSenItem* nodep) { addOp1p(nodep); }
|
||||
void multi(bool flag) { m_multi = true; }
|
||||
// METHODS
|
||||
|
|
@ -2138,8 +2149,8 @@ public:
|
|||
}
|
||||
ASTNODE_NODE_FUNCS(Always)
|
||||
//
|
||||
virtual void dump(ostream& str);
|
||||
AstSenTree* sensesp() const { return op1p()->castSenTree(); } // op1 = Sensitivity list
|
||||
virtual void dump(std::ostream& str);
|
||||
AstSenTree* sensesp() const { return VN_CAST(op1p(), SenTree); } // op1 = Sensitivity list
|
||||
AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate
|
||||
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
|
||||
VAlwaysKwd keyword() const { return m_keyword; }
|
||||
|
|
@ -2159,7 +2170,7 @@ public:
|
|||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
//
|
||||
AstSenTree* sensesp() const { return op1p()->castSenTree(); } // op1 = Sensitivity list
|
||||
AstSenTree* sensesp() const { return VN_CAST(op1p(), SenTree); } // op1 = Sensitivity list
|
||||
AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate
|
||||
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
|
||||
// Special accessors
|
||||
|
|
@ -2345,7 +2356,7 @@ public:
|
|||
}
|
||||
return NULL; }
|
||||
virtual void cloneRelink() { if (m_dataDeclp && m_dataDeclp->clonep()) m_dataDeclp = m_dataDeclp->clonep(); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual int instrCount() const { return 1+2*instrCountLd(); }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
int column() const { return m_column; }
|
||||
|
|
@ -2384,7 +2395,7 @@ public:
|
|||
ASTNODE_NODE_FUNCS(CoverInc)
|
||||
virtual const char* broken() const { BROKEN_RTN(!declp()->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_declp->clonep()) m_declp = m_declp->clonep(); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual int instrCount() const { return 1+2*instrCountLd(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(declp()); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
|
|
@ -2415,7 +2426,7 @@ public:
|
|||
virtual bool isPredictOptimizable() const { return true; }
|
||||
virtual bool isOutputter() const { return false; } // Though the AstCoverInc under this is an outputter
|
||||
// but isPure() true
|
||||
AstCoverInc* incp() const { return op1p()->castCoverInc(); }
|
||||
AstCoverInc* incp() const { return VN_CAST(op1p(), CoverInc); }
|
||||
void incp(AstCoverInc* nodep) { setOp1p(nodep); }
|
||||
AstNode* origp() const { return op2p(); }
|
||||
AstNode* changep() const { return op3p(); }
|
||||
|
|
@ -2525,7 +2536,7 @@ public:
|
|||
AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output
|
||||
string text() const { return m_text; } // * = Text to display
|
||||
void text(const string& text) { m_text=text; }
|
||||
AstScopeName* scopeNamep() const { return op2p()->castScopeName(); }
|
||||
AstScopeName* scopeNamep() const { return VN_CAST(op2p(), ScopeName); }
|
||||
void scopeNamep(AstNode* nodep) { setNOp2p(nodep); }
|
||||
bool formatScopeTracking() const { // Track scopeNamep(); Ok if false positive
|
||||
return (name().find("%m") != string::npos || name().find("%M") != string::npos); }
|
||||
|
|
@ -2554,7 +2565,7 @@ public:
|
|||
m_displayType = dispType;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Display)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual const char* broken() const { BROKEN_RTN(!fmtp()); return NULL; }
|
||||
virtual string verilogKwd() const { return (filep() ? (string)"$f"+(string)displayType().ascii()
|
||||
: (string)"$"+(string)displayType().ascii()); }
|
||||
|
|
@ -2571,7 +2582,7 @@ public:
|
|||
void displayType(AstDisplayType type) { m_displayType = type; }
|
||||
bool addNewline() const { return displayType().addNewline(); } // * = Add a newline for $display
|
||||
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
|
||||
AstSFormatF* fmtp() const { return op1p()->castSFormatF(); }
|
||||
AstSFormatF* fmtp() const { return VN_CAST(op1p(), SFormatF); }
|
||||
AstNode* filep() const { return op3p(); }
|
||||
void filep(AstNodeVarRef* nodep) { setNOp3p(nodep); }
|
||||
};
|
||||
|
|
@ -2600,7 +2611,7 @@ public:
|
|||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
|
||||
AstSFormatF* fmtp() const { return op1p()->castSFormatF(); }
|
||||
AstSFormatF* fmtp() const { return VN_CAST(op1p(), SFormatF); }
|
||||
AstNode* lhsp() const { return op3p(); }
|
||||
void lhsp(AstNode* nodep) { setOp3p(nodep); }
|
||||
};
|
||||
|
|
@ -3080,7 +3091,7 @@ public:
|
|||
ASTNODE_NODE_FUNCS(JumpGo)
|
||||
virtual const char* broken() const { BROKEN_RTN(!labelp()->brokeExistsAbove()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_labelp->clonep()) m_labelp = m_labelp->clonep(); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(labelp()); }
|
||||
virtual bool same(const AstNode* samep) const { // Also same if identical tree structure all the way down, but hard to detect
|
||||
|
|
@ -3100,7 +3111,7 @@ public:
|
|||
addNOp2p(stablesp); addNOp3p(bodysp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(UntilStable)
|
||||
AstVarRef* stablesp() const { return op2p()->castVarRef(); } // op2= list of variables that must become stable
|
||||
AstVarRef* stablesp() const { return VN_CAST(op2p(), VarRef); } // op2= list of variables that must become stable
|
||||
AstNode* bodysp() const { return op3p(); } // op3= body of loop
|
||||
void addStablesp(AstVarRef* newp) { addOp2p(newp); }
|
||||
void addBodysp(AstNode* newp) { addOp3p(newp); }
|
||||
|
|
@ -3172,7 +3183,7 @@ public:
|
|||
m_generate = generate;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Begin)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual string name() const { return m_name; } // * = Block name
|
||||
virtual void name(const string& name) { m_name = name; }
|
||||
// op1 = Statements
|
||||
|
|
@ -3244,7 +3255,7 @@ class AstInitArray : public AstNode {
|
|||
// If default is specified, the vector may be sparse, and not provide each value.
|
||||
// Parents: ASTVAR::init()
|
||||
// Children: CONSTs...
|
||||
deque<uint32_t> m_indices; // Which array index each entry in the list is for (if defaultp)
|
||||
std::deque<uint32_t> m_indices; // Which array index each entry in the list is for (if defaultp)
|
||||
public:
|
||||
AstInitArray(FileLine* fl, AstNodeArrayDType* newDTypep, AstNode* defaultp)
|
||||
: AstNode(fl) {
|
||||
|
|
@ -3377,7 +3388,7 @@ public:
|
|||
ASTNODE_NODE_FUNCS(TraceInc)
|
||||
virtual const char* broken() const { BROKEN_RTN(!declp()->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_declp->clonep()) m_declp = m_declp->clonep(); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dump(std::ostream& str);
|
||||
virtual int instrCount() const { return 10+2*instrCountLd(); }
|
||||
virtual bool hasDType() const { return true; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(declp()); }
|
||||
|
|
@ -3410,7 +3421,7 @@ public:
|
|||
m_sensesp = sensesp;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Active)
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
virtual string name() const { return m_name; }
|
||||
virtual const char* broken() const { BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() {
|
||||
|
|
@ -3424,7 +3435,7 @@ public:
|
|||
AstSenTree* sensesp() const { return m_sensesp; }
|
||||
// op1 = Sensitivity tree, if a clocked block in early stages
|
||||
void sensesStorep(AstSenTree* nodep) { addOp1p(nodep); }
|
||||
AstSenTree* sensesStorep() const { return op1p()->castSenTree(); }
|
||||
AstSenTree* sensesStorep() const { return VN_CAST(op1p(), SenTree); }
|
||||
// op2 = Combo logic
|
||||
AstNode* stmtsp() const { return op2p(); }
|
||||
void addStmtsp(AstNode* nodep) { addOp2p(nodep); }
|
||||
|
|
@ -3448,7 +3459,7 @@ public:
|
|||
AstNode* fromp() const { return op1p(); }
|
||||
AstNode* dimp() const { return op2p(); }
|
||||
AstAttrType attrType() const { return m_attrType; }
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
};
|
||||
|
||||
class AstScopeName : public AstNodeMath {
|
||||
|
|
@ -3469,10 +3480,10 @@ public:
|
|||
virtual string emitVerilog() { return ""; }
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
virtual bool cleanOut() { return true; }
|
||||
AstText* scopeAttrp() const { return op1p()->castText(); }
|
||||
void scopeAttrp(AstNode* nodep) { addOp1p(nodep); }
|
||||
AstText* scopeEntrp() const { return op2p()->castText(); }
|
||||
void scopeEntrp(AstNode* nodep) { addOp2p(nodep); }
|
||||
AstText* scopeAttrp() const { return VN_CAST(op1p(), Text); }
|
||||
void scopeAttrp(AstNode* nodep) { addOp1p(nodep); }
|
||||
AstText* scopeEntrp() const { return VN_CAST(op2p(), Text); }
|
||||
void scopeEntrp(AstNode* nodep) { addOp2p(nodep); }
|
||||
string scopeSymName() const { // Name for __Vscope variable including children
|
||||
return scopeNameFormatter(scopeAttrp()); }
|
||||
string scopeDpiName() const { // Name for DPI import scope
|
||||
|
|
@ -3492,7 +3503,7 @@ public:
|
|||
addNOp1p(bodysp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(UdpTable)
|
||||
AstUdpTableLine* bodysp() const { return op1p()->castUdpTableLine(); } // op1 = List of UdpTableLines
|
||||
AstUdpTableLine* bodysp() const { return VN_CAST(op1p(), UdpTableLine); } // op1 = List of UdpTableLines
|
||||
};
|
||||
|
||||
class AstUdpTableLine : public AstNode {
|
||||
|
|
@ -3896,7 +3907,7 @@ public:
|
|||
virtual bool sizeMattersLhs() {return false;}
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op2p()->castNodeDType(); }
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op2p(), NodeDType); }
|
||||
};
|
||||
|
||||
class AstCastParse : public AstNode {
|
||||
|
|
@ -3955,7 +3966,7 @@ public:
|
|||
virtual V3Hash sameHash() const { return V3Hash(size()); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
return size() == static_cast<const AstCCast*>(samep)->size(); }
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
//
|
||||
int size() const { return m_size; }
|
||||
};
|
||||
|
|
@ -5045,7 +5056,7 @@ class AstReplicate : public AstNodeBiop {
|
|||
private:
|
||||
void init() {
|
||||
if (lhsp()) {
|
||||
if (AstConst* constp=rhsp()->castConst()) {
|
||||
if (const AstConst* constp = VN_CAST(rhsp(), Const)) {
|
||||
dtypeSetLogicSized(lhsp()->width()*constp->toUInt(), lhsp()->width()*constp->toUInt(), AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
|
|
@ -5194,7 +5205,7 @@ public:
|
|||
virtual bool cleanOut() {V3ERROR_NA; return "";}
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Type assigning to
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
AstNode* itemsp() const { return op2p(); } // op2 = AstPatReplicate, AstPatMember, etc
|
||||
|
|
@ -5258,7 +5269,7 @@ public:
|
|||
addNOp2p(bodysp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Clocking)
|
||||
AstNodeSenItem* sensesp() const { return op1p()->castNodeSenItem(); } // op1 = Sensitivity list
|
||||
AstNodeSenItem* sensesp() const { return VN_CAST(op1p(), NodeSenItem); } // op1 = Sensitivity list
|
||||
AstNode* bodysp() const { return op2p(); } // op2 = Body
|
||||
};
|
||||
|
||||
|
|
@ -5278,7 +5289,7 @@ public:
|
|||
}
|
||||
ASTNODE_NODE_FUNCS(PslClocked)
|
||||
virtual bool hasDType() const { return true; } // Used under PslCover, which expects a bool child
|
||||
AstNodeSenItem* sensesp() const { return op1p()->castNodeSenItem(); } // op1 = Sensitivity list
|
||||
AstNodeSenItem* sensesp() const { return VN_CAST(op1p(), NodeSenItem); } // op1 = Sensitivity list
|
||||
AstNode* disablep() const { return op2p(); } // op2 = disable
|
||||
AstNode* propp() const { return op3p(); } // op3 = property
|
||||
};
|
||||
|
|
@ -5302,7 +5313,7 @@ public:
|
|||
virtual bool same(const AstNode* samep) const { return samep->name() == name(); }
|
||||
virtual void name(const string& name) { m_name = name; }
|
||||
AstNode* propp() const { return op1p(); } // op1 = property
|
||||
AstSenTree* sentreep() const { return op2p()->castSenTree(); } // op2 = clock domain
|
||||
AstSenTree* sentreep() const { return VN_CAST(op2p(), SenTree); } // op2 = clock domain
|
||||
void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain
|
||||
AstNode* stmtsp() const { return op4p(); } // op4 = statements
|
||||
};
|
||||
|
|
@ -5432,7 +5443,7 @@ public:
|
|||
virtual string name() const { return m_name; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
bool slow() const { return m_slow; }
|
||||
void slow(bool flag) { m_slow = flag; }
|
||||
bool source() const { return m_source; }
|
||||
|
|
@ -5467,6 +5478,7 @@ private:
|
|||
bool m_dpiExport:1; // From dpi export
|
||||
bool m_dpiExportWrapper:1; // From dpi export; static function with dispatch table
|
||||
bool m_dpiImport:1; // From dpi import
|
||||
bool m_dpiImportWrapper:1; // Wrapper from dpi import
|
||||
public:
|
||||
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType="")
|
||||
: AstNode(fl) {
|
||||
|
|
@ -5488,12 +5500,13 @@ public:
|
|||
m_dpiExport = false;
|
||||
m_dpiExportWrapper = false;
|
||||
m_dpiImport = false;
|
||||
m_dpiImportWrapper = false;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(CFunc)
|
||||
virtual string name() const { return m_name; }
|
||||
virtual const char* broken() const { BROKEN_RTN((m_scopep && !m_scopep->brokeExists())); return NULL; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstCFunc* asamep = static_cast<const AstCFunc*>(samep);
|
||||
|
|
@ -5545,6 +5558,8 @@ public:
|
|||
void dpiExportWrapper(bool flag) { m_dpiExportWrapper = flag; }
|
||||
bool dpiImport() const { return m_dpiImport; }
|
||||
void dpiImport(bool flag) { m_dpiImport = flag; }
|
||||
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
|
||||
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
|
||||
//
|
||||
// If adding node accessors, see below emptyBody
|
||||
AstNode* argsp() const { return op1p(); }
|
||||
|
|
@ -5582,7 +5597,7 @@ public:
|
|||
if (oldp->argsp()) addNOp1p(oldp->argsp()->unlinkFrBackWithNext());
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(CCall)
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
virtual void cloneRelink() { if (m_funcp && m_funcp->clonep()) {
|
||||
m_funcp = m_funcp->clonep();
|
||||
}}
|
||||
|
|
@ -5665,7 +5680,7 @@ public:
|
|||
virtual bool isPredictOptimizable() const { return false; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
AstVarRef* varrefp() const { return op1p()->castVarRef(); } // op1= varref to reset
|
||||
AstVarRef* varrefp() const { return VN_CAST(op1p(), VarRef); } // op1= varref to reset
|
||||
};
|
||||
|
||||
class AstCStmt : public AstNodeStmt {
|
||||
|
|
@ -5685,7 +5700,53 @@ public:
|
|||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const { return true; }
|
||||
void addBodysp(AstNode* nodep) { addNOp1p(nodep); }
|
||||
AstNode* bodysp() const { return op1p(); } // op1= expressions to print
|
||||
AstNode* bodysp() const { return op1p(); } // op1= expressions to print
|
||||
};
|
||||
|
||||
class AstMTaskBody : public AstNode {
|
||||
// Hold statements for each MTask
|
||||
private:
|
||||
ExecMTask* m_execMTaskp;
|
||||
public:
|
||||
explicit AstMTaskBody(FileLine* flp)
|
||||
: AstNode(flp)
|
||||
, m_execMTaskp(NULL) {}
|
||||
ASTNODE_NODE_FUNCS(MTaskBody);
|
||||
virtual const char* broken() const { BROKEN_RTN(!m_execMTaskp); return NULL; }
|
||||
AstNode* stmtsp() const { return op1p(); }
|
||||
void addStmtsp(AstNode* nodep) { addOp1p(nodep); }
|
||||
ExecMTask* execMTaskp() const { return m_execMTaskp; }
|
||||
void execMTaskp(ExecMTask* execMTaskp) { m_execMTaskp = execMTaskp; }
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
};
|
||||
|
||||
class AstExecGraph : public AstNode {
|
||||
// For parallel execution, this node contains a dependency graph. Each
|
||||
// node in the graph is an ExecMTask, which contains a body for the
|
||||
// mtask, which contains a set of AstActive's, each of which calls a
|
||||
// leaf AstCFunc. whew!
|
||||
//
|
||||
// The mtask bodies are also children of this node, so we can visit
|
||||
// them without traversing the graph (it's not always needed to
|
||||
// traverse the graph.)
|
||||
private:
|
||||
V3Graph *m_depGraphp; // contains ExecMTask's
|
||||
public:
|
||||
explicit AstExecGraph(FileLine* fileline);
|
||||
ASTNODE_NODE_FUNCS_NO_DTOR(ExecGraph)
|
||||
virtual ~AstExecGraph();
|
||||
virtual const char* broken() const { BROKEN_RTN(!m_depGraphp); return NULL; }
|
||||
const V3Graph* depGraphp() const { return m_depGraphp; }
|
||||
V3Graph* mutableDepGraphp() { return m_depGraphp; }
|
||||
void addMTaskBody(AstMTaskBody* bodyp) { addOp1p(bodyp); }
|
||||
};
|
||||
|
||||
class AstSplitPlaceholder : public AstNode {
|
||||
public:
|
||||
// Dummy node used within V3Split; never exists outside of V3Split.
|
||||
explicit AstSplitPlaceholder(FileLine* filelinep)
|
||||
: AstNode(filelinep) {}
|
||||
ASTNODE_NODE_FUNCS(SplitPlaceholder)
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -5694,20 +5755,20 @@ public:
|
|||
class AstTypeTable : public AstNode {
|
||||
// Container for hash of standard data types
|
||||
// Children: NODEDTYPEs
|
||||
typedef map<pair<int,int>,AstBasicDType*> LogicMap;
|
||||
typedef std::map<std::pair<int,int>,AstBasicDType*> LogicMap;
|
||||
AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX];
|
||||
//
|
||||
enum { IDX0_LOGIC, IDX0_BIT, _IDX0_MAX };
|
||||
LogicMap m_logicMap[_IDX0_MAX][AstNumeric::_ENUM_MAX]; // uses above IDX enums
|
||||
//
|
||||
typedef map<VBasicTypeKey,AstBasicDType*> DetailedMap;
|
||||
typedef std::map<VBasicTypeKey,AstBasicDType*> DetailedMap;
|
||||
DetailedMap m_detailedMap;
|
||||
public:
|
||||
explicit AstTypeTable(FileLine* fl) : AstNode(fl) {
|
||||
for (int i=0; i<AstBasicDTypeKwd::_ENUM_MAX; ++i) m_basicps[i] = NULL;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(TypeTable)
|
||||
AstNodeDType* typesp() const { return op1p()->castNodeDType();} // op1 = List of dtypes
|
||||
AstNodeDType* typesp() const { return VN_CAST(op1p(), NodeDType);} // op1 = List of dtypes
|
||||
void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); }
|
||||
AstBasicDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd);
|
||||
AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
|
||||
|
|
@ -5717,7 +5778,7 @@ public:
|
|||
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
void clearCache();
|
||||
void repairCache();
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dump(std::ostream& str=std::cout);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -5731,22 +5792,26 @@ private:
|
|||
AstTypeTable* m_typeTablep; // Reference to top type table, for faster lookup
|
||||
AstPackage* m_dollarUnitPkgp;
|
||||
AstCFunc* m_evalp; // The '_eval' function
|
||||
AstExecGraph* m_execGraphp; // Execution MTask graph for threads>1 mode
|
||||
public:
|
||||
AstNetlist()
|
||||
: AstNode(new FileLine("AstRoot",0))
|
||||
, m_typeTablep(NULL)
|
||||
, m_dollarUnitPkgp(NULL)
|
||||
, m_evalp(NULL) { }
|
||||
, m_evalp(NULL)
|
||||
, m_execGraphp(NULL) { }
|
||||
ASTNODE_NODE_FUNCS(Netlist)
|
||||
virtual const char* broken() const {
|
||||
BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists());
|
||||
BROKEN_RTN(m_evalp && !m_evalp->brokeExists());
|
||||
return NULL;
|
||||
}
|
||||
AstNodeModule* modulesp() const { return op1p()->castNodeModule();} // op1 = List of modules
|
||||
AstNodeModule* topModulep() const { return op1p()->castNodeModule(); } // * = Top module in hierarchy (first one added, for now)
|
||||
AstNodeModule* modulesp() const { // op1 = List of modules
|
||||
return VN_CAST(op1p(), NodeModule); }
|
||||
AstNodeModule* topModulep() const { // * = Top module in hierarchy (first one added, for now)
|
||||
return VN_CAST(op1p(), NodeModule); }
|
||||
void addModulep(AstNodeModule* modulep) { addOp1p(modulep); }
|
||||
AstCFile* filesp() const { return op2p()->castCFile();} // op2 = List of files
|
||||
AstCFile* filesp() const { return VN_CAST(op2p(), CFile);} // op2 = List of files
|
||||
void addFilesp(AstCFile* filep) { addOp2p(filep); }
|
||||
AstNode* miscsp() const { return op3p();} // op3 = List of dtypes etc
|
||||
void addMiscsp(AstNode* nodep) { addOp3p(nodep); }
|
||||
|
|
@ -5764,6 +5829,8 @@ public:
|
|||
return m_dollarUnitPkgp; }
|
||||
AstCFunc* evalp() const { return m_evalp; }
|
||||
void evalp(AstCFunc* evalp) { m_evalp = evalp; }
|
||||
AstExecGraph* execGraphp() const { return m_execGraphp; }
|
||||
void execGraphp(AstExecGraph* graphp) { m_execGraphp = graphp; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -77,17 +77,13 @@ private:
|
|||
int m_ifDepth; // Current if depth
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
m_repeatNum = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep) {
|
||||
|
|
@ -107,7 +103,7 @@ private:
|
|||
m_namedScope = "";
|
||||
m_unnamedScope = "";
|
||||
m_ftaskp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_ftaskp = NULL;
|
||||
}
|
||||
m_namedScope = oldScope;
|
||||
|
|
@ -143,7 +139,7 @@ private:
|
|||
}
|
||||
|
||||
// Remap var names and replace lower Begins
|
||||
nodep->stmtsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier");
|
||||
}
|
||||
m_namedScope = oldScope;
|
||||
|
|
@ -184,7 +180,7 @@ private:
|
|||
nodep->unlinkFrBack();
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep) {
|
||||
UINFO(9, " VARXREF "<<nodep<<endl);
|
||||
|
|
@ -204,12 +200,12 @@ private:
|
|||
nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_namedScope));
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverDecl* nodep) {
|
||||
// Don't need to fix path in coverage statements, they're not under
|
||||
// any BEGINs, but V3Coverage adds them all under the module itself.
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
// VISITORS - LINT CHECK
|
||||
virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered
|
||||
|
|
@ -223,11 +219,11 @@ private:
|
|||
nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once
|
||||
m_ifDepth = -1;
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_ifDepth = prevIfDepth;
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
|
|
@ -237,7 +233,7 @@ public:
|
|||
m_ftaskp = NULL;
|
||||
m_repeatNum = 0;
|
||||
m_ifDepth = 0;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~BeginVisitor() {}
|
||||
};
|
||||
|
|
@ -257,14 +253,14 @@ private:
|
|||
UINFO(9, " relinkFTask "<<nodep<<endl);
|
||||
nodep->name(nodep->taskp()->name());
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
if (nodep->varp()->user1()) { // It was converted
|
||||
UINFO(9, " relinVarRef "<<nodep<<endl);
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstIfaceRefDType* nodep) {
|
||||
// May have changed cell names
|
||||
|
|
@ -272,16 +268,16 @@ private:
|
|||
UINFO(8," IFACEREFDTYPE "<<nodep<<endl);
|
||||
if (nodep->cellp()) nodep->cellName(nodep->cellp()->name());
|
||||
UINFO(8," rename to "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
BeginRelinkVisitor(AstNetlist* nodep, BeginState*) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~BeginRelinkVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ private:
|
|||
AstUser1InUse m_inuser1;
|
||||
|
||||
// TYPES
|
||||
typedef vector<AstCFunc*> CFuncVec;
|
||||
typedef std::vector<AstCFunc*> CFuncVec;
|
||||
|
||||
// STATE
|
||||
int m_likely; // Excuses for branch likely taken
|
||||
|
|
@ -57,11 +57,7 @@ private:
|
|||
CFuncVec m_cfuncsp; // List of all tasks
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void reset() {
|
||||
m_likely = false;
|
||||
|
|
@ -82,12 +78,12 @@ private:
|
|||
{
|
||||
// Do if
|
||||
reset();
|
||||
nodep->ifsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
int ifLikely = m_likely;
|
||||
int ifUnlikely = m_unlikely;
|
||||
// Do else
|
||||
reset();
|
||||
nodep->elsesp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
int elseLikely = m_likely;
|
||||
int elseUnlikely = m_unlikely;
|
||||
// Compute
|
||||
|
|
@ -104,16 +100,16 @@ private:
|
|||
virtual void visit(AstCCall* nodep) {
|
||||
checkUnlikely(nodep);
|
||||
nodep->funcp()->user1Inc();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
checkUnlikely(nodep);
|
||||
m_cfuncsp.push_back(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
checkUnlikely(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// METHODS
|
||||
|
|
@ -130,7 +126,7 @@ public:
|
|||
// CONSTUCTORS
|
||||
explicit BranchVisitor(AstNetlist* nodep) {
|
||||
reset();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
calc_tasks();
|
||||
}
|
||||
virtual ~BranchVisitor() {}
|
||||
|
|
|
|||
|
|
@ -155,9 +155,9 @@ public:
|
|||
// 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()) {
|
||||
cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": ");
|
||||
((AstNode*)(it->first))->AstNode::dump(cerr);
|
||||
cerr<<endl;
|
||||
std::cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": ");
|
||||
((AstNode*)(it->first))->AstNode::dump(std::cerr);
|
||||
std::cerr<<endl;
|
||||
V3Error::incErrors();
|
||||
}
|
||||
it->second |= FLAG_LEAKED;
|
||||
|
|
@ -193,7 +193,7 @@ private:
|
|||
// METHODS
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
|
||||
nodep->iterateChildrenConst(*this);
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) {
|
||||
|
|
@ -202,7 +202,7 @@ private:
|
|||
public:
|
||||
// CONSTUCTORS
|
||||
explicit BrokenMarkVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~BrokenMarkVisitor() {}
|
||||
};
|
||||
|
|
@ -212,7 +212,7 @@ public:
|
|||
|
||||
class BrokenCheckVisitor : public AstNVisitor {
|
||||
private:
|
||||
void checkWidthMin(AstNode* nodep) {
|
||||
void checkWidthMin(const AstNode* nodep) {
|
||||
if (nodep->width() != nodep->widthMin()
|
||||
&& v3Global.widthMinUsage()==VWidthMinUsage::MATCHES_WIDTH) {
|
||||
nodep->v3fatalSrc("Width != WidthMin");
|
||||
|
|
@ -225,10 +225,9 @@ private:
|
|||
}
|
||||
if (nodep->dtypep()) {
|
||||
if (!nodep->dtypep()->brokeExists()) {
|
||||
nodep->v3fatalSrc("Broken link in node->dtypep() to "<<(void*)nodep->dtypep());
|
||||
} else if (!nodep->dtypep()->castNodeDType()) {
|
||||
nodep->v3fatalSrc("Non-dtype link in node->dtypep() to "<<(void*)nodep->dtypep());
|
||||
}
|
||||
nodep->v3fatalSrc("Broken link in node->dtypep() to "<<(void*)nodep->dtypep()); }
|
||||
else if (!VN_IS(nodep->dtypep(), NodeDType)) {
|
||||
nodep->v3fatalSrc("Non-dtype link in node->dtypep() to "<<(void*)nodep->dtypep()); }
|
||||
}
|
||||
if (v3Global.assertDTypesResolved()) {
|
||||
if (nodep->hasDType()) {
|
||||
|
|
@ -237,18 +236,18 @@ private:
|
|||
if (nodep->dtypep()) nodep->v3fatalSrc("DType on node without hasDType(): "<<nodep->prettyTypeName());
|
||||
}
|
||||
if (nodep->getChildDTypep()) nodep->v3fatalSrc("childDTypep() non-null on node after should have removed");
|
||||
if (AstNodeDType* dnodep = nodep->castNodeDType()) checkWidthMin(dnodep);
|
||||
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
|
||||
}
|
||||
checkWidthMin(nodep);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
iterateChildrenConst(nodep);
|
||||
BrokenTable::setUnder(nodep,false);
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
processAndIterate(nodep);
|
||||
if (v3Global.assertDTypesResolved()
|
||||
&& nodep->brokeLhsMustBeLvalue()
|
||||
&& nodep->lhsp()->castNodeVarRef()
|
||||
&& !nodep->lhsp()->castNodeVarRef()->lvalue()) {
|
||||
&& VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()) {
|
||||
nodep->v3fatalSrc("Assignment LHS is not an lvalue");
|
||||
}
|
||||
}
|
||||
|
|
@ -258,7 +257,7 @@ private:
|
|||
public:
|
||||
// CONSTUCTORS
|
||||
explicit BrokenCheckVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~BrokenCheckVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -113,9 +113,9 @@ void V3CCtors::evalAsserts() {
|
|||
funcp->ifdef("VL_DEBUG");
|
||||
modp->addStmtp(funcp);
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* varp = np->castVar()) {
|
||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||
if (varp->isPrimaryIn() && !varp->isSc()) {
|
||||
if (AstBasicDType* basicp = varp->dtypeSkipRefp()->castBasicDType()) {
|
||||
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
|
||||
int storedWidth = basicp->widthAlignBytes() * 8;
|
||||
int lastWordWidth = varp->width() % storedWidth;
|
||||
if (lastWordWidth != 0) {
|
||||
|
|
@ -145,12 +145,12 @@ void V3CCtors::evalAsserts() {
|
|||
void V3CCtors::cctorsAll() {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
evalAsserts();
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=modp->nextp()->castNodeModule()) {
|
||||
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");
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* varp = np->castVar()) {
|
||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef()) {
|
||||
var_reset.add(new AstCReset(varp->fileline(), new AstVarRef(varp->fileline(), varp, true)));
|
||||
}
|
||||
|
|
@ -162,7 +162,7 @@ void V3CCtors::cctorsAll() {
|
|||
(modp, "_configure_coverage", EmitCBaseVisitor::symClassVar()+ ", bool first", "vlSymsp, first",
|
||||
"if (0 && vlSymsp && first) {} // Prevent unused\n");
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstCoverDecl* coverp = np->castCoverDecl()) {
|
||||
if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) {
|
||||
AstNode* backp = coverp->backp();
|
||||
coverp->unlinkFrBack();
|
||||
configure_coverage.add(coverp);
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@
|
|||
// Or, converts to a if/else tree.
|
||||
// FUTURES:
|
||||
// Large 16+ bit tables with constants and no masking (address muxes)
|
||||
// Enter all into multimap, sort by value and use a tree of < and == compares.
|
||||
// Enter all into std::multimap, sort by value and use a tree of < and == compares.
|
||||
// "Diagonal" find of {rightmost,leftmost} bit {set,clear}
|
||||
// Ignoring mask, check each value is unique (using multimap as above?)
|
||||
// Ignoring mask, check each value is unique (using std::multimap as above?)
|
||||
// Each branch is then mask-and-compare operation (IE <000000001_000000000 at midpoint.)
|
||||
//
|
||||
//*************************************************************************
|
||||
|
|
@ -59,19 +59,16 @@ class CaseLintVisitor : public AstNVisitor {
|
|||
private:
|
||||
AstNodeCase* m_caseExprp; // Under a CASE value node, if so the relevant case statement
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
virtual void visit(AstNodeCase* nodep) {
|
||||
if (nodep->castCase() && nodep->castCase()->casex()) {
|
||||
if (VN_IS(nodep, Case) && VN_CAST(nodep, Case)->casex()) {
|
||||
nodep->v3warn(CASEX,"Suggest casez (with ?'s) in place of casex (with X's)");
|
||||
}
|
||||
// Detect multiple defaults
|
||||
bool hitDefault = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) {
|
||||
if (hitDefault) {
|
||||
nodep->v3error("Multiple default statements in case statement.");
|
||||
|
|
@ -83,9 +80,9 @@ private:
|
|||
// Check for X/Z in non-casex statements
|
||||
{
|
||||
m_caseExprp = nodep;
|
||||
nodep->exprp()->accept(*this);
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
itemp->condsp()->iterateAndNext(*this);
|
||||
iterate(nodep->exprp());
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
iterateAndNextNull(itemp->condsp());
|
||||
}
|
||||
m_caseExprp = NULL;
|
||||
}
|
||||
|
|
@ -93,12 +90,12 @@ private:
|
|||
virtual void visit(AstConst* nodep) {
|
||||
// See also neverItem
|
||||
if (m_caseExprp && nodep->num().isFourState()) {
|
||||
if (m_caseExprp->castGenCase()) {
|
||||
if (VN_IS(m_caseExprp, GenCase)) {
|
||||
nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')");
|
||||
} else if (m_caseExprp->castCase() && m_caseExprp->castCase()->casex()) {
|
||||
} 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 (m_caseExprp->castCase() && (m_caseExprp->castCase()->casez()
|
||||
|| m_caseExprp->castCase()->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)");
|
||||
}
|
||||
|
|
@ -108,13 +105,13 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CaseLintVisitor(AstNodeCase* nodep) {
|
||||
m_caseExprp = NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CaseLintVisitor() {}
|
||||
};
|
||||
|
|
@ -140,22 +137,18 @@ private:
|
|||
AstNode* m_valueItem[1<<CASE_OVERLAP_WIDTH]; // For each possible value, the case branch we need
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
bool isCaseTreeFast(AstCase* nodep) {
|
||||
int width = 0;
|
||||
bool opaque = false;
|
||||
m_caseItems = 0;
|
||||
m_caseNoOverlapsAllCovered = true;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
if (icondp->width() > width) width = icondp->width();
|
||||
if (icondp->isDouble()) opaque = true;
|
||||
if (!icondp->castConst()) width = CASE_BARF; // Can't parse; not a constant
|
||||
if (!VN_IS(icondp, Const)) width = CASE_BARF; // Can't parse; not a constant
|
||||
m_caseItems++;
|
||||
}
|
||||
}
|
||||
|
|
@ -170,10 +163,10 @@ private:
|
|||
// 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=itemp->nextp()->castCaseItem()) {
|
||||
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: ");
|
||||
AstConst* iconstp = icondp->castConst();
|
||||
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||
if (!iconstp) nodep->v3fatalSrc("above 'can't parse' should have caught this");
|
||||
if (neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
|
|
@ -189,7 +182,7 @@ private:
|
|||
if (!m_valueItem[i]) {
|
||||
m_valueItem[i] = itemp;
|
||||
} else if (!itemp->ignoreOverlap() && !bitched) {
|
||||
itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<<hex<<i<<")");
|
||||
itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<<std::hex<<i<<")");
|
||||
bitched = true;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
}
|
||||
|
|
@ -206,7 +199,7 @@ private:
|
|||
}
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
if (!m_valueItem[i]) {
|
||||
nodep->v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<<hex<<i<<")");
|
||||
nodep->v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<<std::hex<<i<<")");
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -214,8 +207,8 @@ private:
|
|||
if (m_caseItems <= 3) return false; // Not worth simplifing
|
||||
// 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++) {
|
||||
m_valueItem[i] = m_valueItem[i]->castCaseItem()->bodysp();
|
||||
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
|
||||
}
|
||||
|
|
@ -280,7 +273,7 @@ private:
|
|||
if (debug()>=9) {
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
if (AstNode* itemp = m_valueItem[i]) {
|
||||
UINFO(9,"Value "<<hex<<i<<" "<<itemp<<endl);
|
||||
UINFO(9,"Value "<<std::hex<<i<<" "<<itemp<<endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -310,7 +303,7 @@ private:
|
|||
// the appropriate IF AND terms.
|
||||
if (debug()>=9) nodep->dumpTree(cout," _comp_IN: ");
|
||||
bool hadDefault = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
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()));
|
||||
|
|
@ -324,14 +317,14 @@ private:
|
|||
icondp->unlinkFrBack();
|
||||
|
||||
AstNode* condp = NULL; // Default is to use and1p/and2p
|
||||
AstConst* iconstp = icondp->castConst();
|
||||
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||
if (iconstp && neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
icondp->deleteTree(); VL_DANGLING(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());
|
||||
} else if (AstInsideRange* irangep = icondp->castInsideRange()) {
|
||||
} else if (AstInsideRange* irangep = VN_CAST(icondp, InsideRange)) {
|
||||
// Similar logic in V3Width::visit(AstInside)
|
||||
AstNode* ap = AstGte::newTyped(itemp->fileline(),
|
||||
cexprp->cloneTree(false),
|
||||
|
|
@ -387,7 +380,7 @@ private:
|
|||
AstNode* grouprootp = NULL;
|
||||
AstIf* groupnextp = NULL;
|
||||
AstIf* itemnextp = NULL;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
||||
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
|
||||
|
|
@ -453,7 +446,7 @@ private:
|
|||
// VISITORS
|
||||
virtual void visit(AstCase* nodep) {
|
||||
V3Case::caseLint(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (debug()>=9) nodep->dumpTree(cout," case_old: ");
|
||||
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
|
||||
// It's a simple priority encoder or complete statement
|
||||
|
|
@ -468,14 +461,19 @@ private:
|
|||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CaseVisitor(AstNetlist* nodep) {
|
||||
m_caseWidth = 0;
|
||||
m_caseItems = 0;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
nodep->accept(*this);
|
||||
for (uint32_t i=0; i<(1UL<<CASE_OVERLAP_WIDTH); ++i) {
|
||||
m_valueItem[i] = NULL;
|
||||
}
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CaseVisitor() {
|
||||
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
|
||||
|
|
|
|||
|
|
@ -65,11 +65,7 @@ private:
|
|||
// STATE
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
|
||||
UINFO(4," NeedCast "<<nodep<<endl);
|
||||
|
|
@ -101,26 +97,26 @@ private:
|
|||
// Otherwise a (uint64)(a>b) would return wrong value, as
|
||||
// less than has undeterministic signedness.
|
||||
if (nodep->isQuad() && !nodep->lhsp()->isQuad()
|
||||
&& !nodep->lhsp()->castCCast()) {
|
||||
&& !VN_IS(nodep->lhsp(), CCast)) {
|
||||
insertCast(nodep->lhsp(), VL_WORDSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeUniop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||
}
|
||||
virtual void visit(AstNodeBiop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1()
|
||||
| nodep->rhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
|
||||
}
|
||||
virtual void visit(AstNodeTriop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1()
|
||||
| nodep->rhsp()->user1()
|
||||
| nodep->thsp()->user1());
|
||||
|
|
@ -129,12 +125,12 @@ private:
|
|||
if (nodep->sizeMattersThs()) insureCast(nodep->thsp());
|
||||
}
|
||||
virtual void visit(AstCCast* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureLower32Cast(nodep);
|
||||
nodep->user1(1);
|
||||
}
|
||||
virtual void visit(AstNegate* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->lhsp()->widthMin()==1) {
|
||||
// We want to avoid a GCC "converting of negative value" warning
|
||||
|
|
@ -147,9 +143,9 @@ private:
|
|||
}
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
if (!nodep->lvalue()
|
||||
&& !nodep->backp()->castCCast()
|
||||
&& nodep->backp()->castNodeMath()
|
||||
&& !nodep->backp()->castArraySel()
|
||||
&& !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
|
||||
|
|
@ -171,13 +167,13 @@ private:
|
|||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CastVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CastVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -51,11 +51,7 @@
|
|||
|
||||
class CdcBaseVisitor : public AstNVisitor {
|
||||
public:
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -77,6 +73,7 @@ public:
|
|||
, m_asyncPath(false) {}
|
||||
virtual ~CdcEitherVertex() {}
|
||||
// ACCESSORS
|
||||
virtual FileLine* fileline() const { return nodep()->fileline(); }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
AstSenTree* srcDomainp() const { return m_srcDomainp; }
|
||||
|
|
@ -135,7 +132,7 @@ private:
|
|||
// NODE STATE
|
||||
//Entire netlist:
|
||||
// {statement}Node::user3 -> bool, indicating not hazard
|
||||
ofstream* m_ofp; // Output file
|
||||
std::ofstream* m_ofp; // Output file
|
||||
string m_prefix;
|
||||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
|
|
@ -145,22 +142,22 @@ private:
|
|||
*m_ofp<<nodep->prettyTypeName()<<" "<<endl;
|
||||
string lastPrefix = m_prefix;
|
||||
m_prefix = lastPrefix + "1:";
|
||||
nodep->op1p()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->op1p());
|
||||
m_prefix = lastPrefix + "2:";
|
||||
nodep->op2p()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->op2p());
|
||||
m_prefix = lastPrefix + "3:";
|
||||
nodep->op3p()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->op3p());
|
||||
m_prefix = lastPrefix + "4:";
|
||||
nodep->op4p()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->op4p());
|
||||
m_prefix = lastPrefix;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
CdcDumpVisitor(AstNode* nodep, ofstream* ofp, const string& prefix) {
|
||||
CdcDumpVisitor(AstNode* nodep, std::ofstream* ofp, const string& prefix) {
|
||||
m_ofp = ofp;
|
||||
m_prefix = prefix;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CdcDumpVisitor() {}
|
||||
};
|
||||
|
|
@ -173,7 +170,7 @@ private:
|
|||
size_t m_maxFilenameLen;
|
||||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
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;
|
||||
|
|
@ -187,7 +184,7 @@ public:
|
|||
explicit CdcWidthVisitor(AstNode* nodep) {
|
||||
m_maxLineno = 0;
|
||||
m_maxFilenameLen = 0;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CdcWidthVisitor() {}
|
||||
// ACCESSORS
|
||||
|
|
@ -225,7 +222,7 @@ private:
|
|||
bool m_inDly; // In delayed assign
|
||||
int m_inSenItem; // Number of senitems
|
||||
string m_ofFilename; // Output filename
|
||||
ofstream* m_ofp; // Output file
|
||||
std::ofstream* m_ofp; // Output file
|
||||
uint32_t m_userGeneration; // Generation count to avoid slow userClearVertices
|
||||
int m_filelineWidth; // Characters in longest fileline
|
||||
|
||||
|
|
@ -241,7 +238,7 @@ private:
|
|||
m_logicVertexp->dstDomainp(m_domainp);
|
||||
m_logicVertexp->dstDomainSet(true);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_logicVertexp = NULL;
|
||||
|
||||
if (0 && debug()>=9) {
|
||||
|
|
@ -284,7 +281,7 @@ private:
|
|||
nodep->v3warnCode(code,msg);
|
||||
if (!told_file) {
|
||||
told_file = 1;
|
||||
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;
|
||||
}
|
||||
|
|
@ -448,7 +445,7 @@ private:
|
|||
// See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp)
|
||||
AstNode* nodep = vertexp->nodep();
|
||||
string front = pad(filelineWidth(),nodep->fileline()->ascii()+":")+" "+prefix+" +- ";
|
||||
if (nodep->castVarScope()) {
|
||||
if (VN_IS(nodep, VarScope)) {
|
||||
*m_ofp<<front<<"Variable: "<<nodep->prettyName()<<endl;
|
||||
}
|
||||
else {
|
||||
|
|
@ -500,7 +497,7 @@ private:
|
|||
if (ofp->fail()) v3fatal("Can't write "<<filename);
|
||||
*ofp<<"Edge Report for "<<v3Global.opt.prefix()<<endl;
|
||||
|
||||
deque<string> report; // Sort output by name
|
||||
std::deque<string> report; // Sort output by name
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
AstVar* varp = vvertexp->varScp()->varp();
|
||||
|
|
@ -508,26 +505,26 @@ private:
|
|||
const char* whatp = "wire";
|
||||
if (varp->isPrimaryIO()) whatp = (varp->isInout()?"inout":varp->isInput()?"input":"output");
|
||||
|
||||
ostringstream os;
|
||||
os.setf(ios::left);
|
||||
std::ostringstream os;
|
||||
os.setf(std::ios::left);
|
||||
// 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<<" "<<setw(20)<<fname;
|
||||
os<<" "<<setw(8)<<whatp;
|
||||
os<<" "<<setw(40)<<vvertexp->varScp()->prettyName();
|
||||
os<<" "<<std::setw(20)<<fname;
|
||||
os<<" "<<std::setw(8)<<whatp;
|
||||
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<<setw(0);
|
||||
os<<std::setw(0);
|
||||
os<<endl;
|
||||
report.push_back(os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
stable_sort(report.begin(), report.end());
|
||||
for (deque<string>::iterator it = report.begin(); it!=report.end(); ++it) {
|
||||
for (std::deque<string>::iterator it = report.begin(); it!=report.end(); ++it) {
|
||||
*ofp << *it;
|
||||
}
|
||||
}
|
||||
|
|
@ -541,7 +538,7 @@ private:
|
|||
// Variables from flops already are domained
|
||||
if (traceDests ? vertexp->dstDomainSet() : vertexp->srcDomainSet()) return; // Fully computed
|
||||
|
||||
typedef set<AstSenTree*> SenSet;
|
||||
typedef std::set<AstSenTree*> SenSet;
|
||||
SenSet senouts; // List of all sensitivities for new signal
|
||||
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
if (vvertexp) {} // Unused
|
||||
|
|
@ -584,7 +581,7 @@ private:
|
|||
}
|
||||
// If multiple domains need to do complicated optimizations
|
||||
if (senedited) {
|
||||
senoutp = V3Const::constifyExpensiveEdit(senoutp)->castSenTree();
|
||||
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
|
||||
|
|
@ -606,14 +603,14 @@ private:
|
|||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
UINFO(4," SCOPE "<<nodep<<endl);
|
||||
m_scopep = nodep;
|
||||
m_logicVertexp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_scopep = NULL;
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
|
|
@ -657,14 +654,14 @@ private:
|
|||
}
|
||||
virtual void visit(AstAssignDly* nodep) {
|
||||
m_inDly = true;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_inDly = false;
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) {
|
||||
// Note we look at only AstSenItems, not AstSenGate's
|
||||
// The gating term of a AstSenGate is normal logic
|
||||
m_inSenItem = true;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_inSenItem = false;
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) {
|
||||
|
|
@ -691,21 +688,21 @@ private:
|
|||
// Math that shouldn't cause us to clear hazard
|
||||
virtual void visit(AstConst* nodep) { }
|
||||
virtual void visit(AstReplicate* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstConcat* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNot* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstSel* nodep) {
|
||||
if (!nodep->lsbp()->castConst()) setNodeHazard(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
if (!VN_IS(nodep->lsbp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeSel* nodep) {
|
||||
if (!nodep->bitp()->castConst()) setNodeHazard(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
if (!VN_IS(nodep->bitp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Ignores
|
||||
|
|
@ -718,10 +715,10 @@ private:
|
|||
// Default
|
||||
virtual void visit(AstNodeMath* nodep) {
|
||||
setNodeHazard(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -747,7 +744,7 @@ public:
|
|||
*m_ofp<<"repeating recursively forwards to the destination flop(s).\n";
|
||||
*m_ofp<<"%% Indicates the operator considered potentially hazardous.\n";
|
||||
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
analyze();
|
||||
if (debug()>=1) edgeReport(); // Not useful to users at the moment
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ public:
|
|||
if (!m_tlChgFuncp->stmtsp()) {
|
||||
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
|
||||
} else {
|
||||
AstCReturn* returnp = m_tlChgFuncp->stmtsp()->castCReturn();
|
||||
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
|
||||
if (!returnp) m_scopetopp->v3fatalSrc("Lost CReturn in top change function");
|
||||
// This is currently using AstLogOr which will shortcut the evaluation if
|
||||
// any function returns true. This is likely what we want and is similar to the logic already in use
|
||||
|
|
@ -159,7 +159,7 @@ private:
|
|||
m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
|
||||
m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
|
||||
|
||||
nodep->subDTypep()->skipRefp()->accept(*this);
|
||||
iterate(nodep->subDTypep()->skipRefp());
|
||||
|
||||
m_varEqnp->deleteTree();
|
||||
m_newLvEqnp->deleteTree();
|
||||
|
|
@ -181,7 +181,7 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
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): "
|
||||
|
|
@ -209,7 +209,7 @@ public:
|
|||
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true);
|
||||
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false);
|
||||
}
|
||||
vscp->dtypep()->skipRefp()->accept(*this);
|
||||
iterate(vscp->dtypep()->skipRefp());
|
||||
m_varEqnp->deleteTree();
|
||||
m_newLvEqnp->deleteTree();
|
||||
m_newRvEqnp->deleteTree();
|
||||
|
|
@ -231,11 +231,7 @@ private:
|
|||
ChangedState* m_statep; // Shared state across visitors
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void genChangeDet(AstVarScope* vscp) {
|
||||
vscp->v3warn(IMPERFECTSCH,"Imperfect scheduling of variable: "<<vscp);
|
||||
|
|
@ -248,7 +244,7 @@ private:
|
|||
if (nodep->isTop()) {
|
||||
m_statep->m_topModp = nodep;
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
|
|
@ -271,7 +267,7 @@ private:
|
|||
m_statep->maybeCreateChgFuncp();
|
||||
m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false));
|
||||
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep) {
|
||||
if (nodep->isCircular()) {
|
||||
|
|
@ -287,14 +283,14 @@ private:
|
|||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
ChangedVisitor(AstNetlist* nodep, ChangedState* statep) {
|
||||
m_statep = statep;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ChangedVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -59,11 +59,7 @@ private:
|
|||
AstNodeModule* m_modp;
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// Width resetting
|
||||
int cppWidth(AstNode* nodep) {
|
||||
|
|
@ -78,7 +74,7 @@ private:
|
|||
if (old_dtypep->width() != width) {
|
||||
// Since any given dtype's cppWidth() is the same, we can just
|
||||
// remember one convertion for each, and reuse it
|
||||
if (AstNodeDType* new_dtypep = old_dtypep->user3p()->castNodeDType()) {
|
||||
if (AstNodeDType* new_dtypep = VN_CAST(old_dtypep->user3p(), NodeDType)) {
|
||||
nodep->dtypep(new_dtypep);
|
||||
} else {
|
||||
nodep->dtypeChgWidth(width, nodep->widthMin());
|
||||
|
|
@ -90,8 +86,8 @@ private:
|
|||
}
|
||||
void computeCppWidth (AstNode* nodep) {
|
||||
if (!nodep->user2() && nodep->hasDType()) {
|
||||
if (nodep->castVar() || nodep->castNodeDType() // Don't want to change variable widths!
|
||||
|| nodep->dtypep()->skipRefp()->castUnpackArrayDType()) { // Or arrays
|
||||
if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { // Or arrays
|
||||
} else {
|
||||
setCppWidth(nodep);
|
||||
}
|
||||
|
|
@ -148,7 +144,7 @@ private:
|
|||
|
||||
// Base type handling methods
|
||||
void operandBiop(AstNodeBiop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
|
|
@ -159,7 +155,7 @@ private:
|
|||
//no setClean.. must do it in each user routine.
|
||||
}
|
||||
void operandTriop(AstNodeTriop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
|
|
@ -176,11 +172,11 @@ private:
|
|||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeUniop* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
insureClean(nodep->lhsp());
|
||||
|
|
@ -204,12 +200,12 @@ private:
|
|||
setClean (nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
||||
}
|
||||
virtual void visit(AstNodeMath* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
setClean (nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanRhs()) {
|
||||
insureClean(nodep->rhsp());
|
||||
|
|
@ -226,53 +222,53 @@ private:
|
|||
setClean (nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstUCFunc* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
setClean (nodep, false);
|
||||
// We always clean, as we don't trust those pesky users.
|
||||
if (!nodep->backp()->castAnd()) {
|
||||
if (!VN_IS(nodep->backp(), And)) {
|
||||
insertClean(nodep);
|
||||
}
|
||||
insureCleanAndNext (nodep->bodysp());
|
||||
}
|
||||
virtual void visit(AstTraceInc* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureCleanAndNext (nodep->valuep());
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) {
|
||||
// No cleaning, or would loose pointer to enum
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstParamTypeDType* nodep) {
|
||||
// No cleaning, or would loose pointer to enum
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Control flow operators
|
||||
virtual void visit(AstNodeCond* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureClean(nodep->condp());
|
||||
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
|
||||
}
|
||||
virtual void visit(AstWhile* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureClean(nodep->condp());
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureClean(nodep->condp());
|
||||
}
|
||||
virtual void visit(AstSFormatF* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureCleanAndNext (nodep->exprsp());
|
||||
setClean(nodep, true); // generates a string, so not relevant
|
||||
}
|
||||
virtual void visit(AstUCStmt* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureCleanAndNext (nodep->bodysp());
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
insureCleanAndNext (nodep->argsp());
|
||||
setClean (nodep, true);
|
||||
}
|
||||
|
|
@ -280,14 +276,15 @@ private:
|
|||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CleanVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
m_modp = NULL;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CleanVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -51,11 +51,7 @@
|
|||
|
||||
class GaterBaseVisitor : public AstNVisitor {
|
||||
protected:
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -196,7 +192,7 @@ private:
|
|||
bool m_isSimple; // Set false when we know it isn't simple
|
||||
// METHODS
|
||||
inline void okIterate(AstNode* nodep) {
|
||||
if (m_isSimple) nodep->iterateChildren(*this);
|
||||
if (m_isSimple) iterateChildren(nodep);
|
||||
}
|
||||
// VISITORS
|
||||
virtual void visit(AstOr* nodep) { okIterate(nodep); }
|
||||
|
|
@ -213,13 +209,13 @@ private:
|
|||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
m_isSimple = false;
|
||||
//nodep->iterateChildren(*this);
|
||||
//iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit GaterCondVisitor(AstNode* nodep) {
|
||||
m_isSimple = true;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~GaterCondVisitor() {}
|
||||
// PUBLIC METHODS
|
||||
|
|
@ -283,7 +279,7 @@ class GaterBodyVisitor : public GaterBaseVisitor {
|
|||
uint32_t childstate;
|
||||
{
|
||||
m_state = STATE_UNKNOWN;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
childstate = m_state;
|
||||
}
|
||||
m_state = oldstate;
|
||||
|
|
@ -305,7 +301,7 @@ class GaterBodyVisitor : public GaterBaseVisitor {
|
|||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
|
|
@ -315,7 +311,7 @@ public:
|
|||
m_state = STATE_UNKNOWN;
|
||||
m_cloning = false;
|
||||
if (debug()>=9) nodep->dumpTree(cout," GateBodyIn: ");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
if (debug()>=9) nodep->dumpTree(cout," GateBodyOut: ");
|
||||
// If there's no statements we shouldn't have had a resulting graph
|
||||
// vertex asking for this creation
|
||||
|
|
@ -672,7 +668,7 @@ class GaterVisitor : public GaterBaseVisitor {
|
|||
AstSenTree* sensesp = nodep->sensesp()->cloneTree(true);
|
||||
#else
|
||||
// Make a SenGate
|
||||
AstSenItem* oldsenitemsp = nodep->sensesp()->sensesp()->castSenItem();
|
||||
AstSenItem* oldsenitemsp = VN_CAST(nodep->sensesp()->sensesp(), SenItem);
|
||||
if (!oldsenitemsp) nodep->v3fatalSrc("SenTree doesn't have any SenItem under it");
|
||||
|
||||
AstSenTree* sensesp = new AstSenTree(nodep->fileline(),
|
||||
|
|
@ -802,17 +798,17 @@ class GaterVisitor : public GaterBaseVisitor {
|
|||
GaterIfVertex* vertexp = new GaterIfVertex(&m_graph, nodep);
|
||||
new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue);
|
||||
{
|
||||
nodep->condp()->iterateAndNext(*this); // directlyUnder stays as-is
|
||||
iterateAndNextNull(nodep->condp()); // directlyUnder stays as-is
|
||||
}
|
||||
{
|
||||
m_aboveVertexp = vertexp; // Vars will point at this edge
|
||||
m_aboveTrue = VU_IF;
|
||||
nodep->ifsp()->iterateAndNext(*this); // directlyUnder stays as-is (true)
|
||||
iterateAndNextNull(nodep->ifsp()); // directlyUnder stays as-is (true)
|
||||
}
|
||||
{
|
||||
m_aboveVertexp = vertexp; // Vars will point at this edge
|
||||
m_aboveTrue = VU_ELSE;
|
||||
nodep->elsesp()->iterateAndNext(*this); // directlyUnder stays as-is (true)
|
||||
iterateAndNextNull(nodep->elsesp()); // directlyUnder stays as-is (true)
|
||||
}
|
||||
m_aboveVertexp = lastabovep;
|
||||
m_aboveTrue = lasttrue;
|
||||
|
|
@ -857,7 +853,7 @@ class GaterVisitor : public GaterBaseVisitor {
|
|||
AstVarScope* lastvscp = m_stmtVscp;
|
||||
bool lastpli = m_stmtInPli;
|
||||
m_directlyUnderAlw = under;
|
||||
if (nodep->castNodeStmt()) { // Restored below
|
||||
if (VN_IS(nodep, NodeStmt)) { // Restored below
|
||||
UINFO(9," Stmt: "<<nodep<<endl);
|
||||
m_stmtVscp = NULL;
|
||||
m_stmtInPli = false;
|
||||
|
|
@ -868,10 +864,10 @@ class GaterVisitor : public GaterBaseVisitor {
|
|||
scoreboardPli(nodep);
|
||||
}
|
||||
{
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_directlyUnderAlw = lastdua;
|
||||
if (nodep->castNodeStmt()) { // Reset what set above; else propagate up to above statement
|
||||
if (VN_IS(nodep, NodeStmt)) { // Reset what set above; else propagate up to above statement
|
||||
m_stmtVscp = lastvscp;
|
||||
m_stmtInPli = lastpli;
|
||||
}
|
||||
|
|
@ -882,7 +878,7 @@ public:
|
|||
explicit GaterVisitor(AstNetlist* nodep) {
|
||||
// AstAlways visitor does the real work, so most zeroing needs to be in clear()
|
||||
clear();
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
void clear() {
|
||||
m_nonopt = "";
|
||||
|
|
|
|||
|
|
@ -68,13 +68,10 @@ private:
|
|||
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
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
|
||||
if (vscp->user1p()) return ((AstVarScope*)vscp->user1p());
|
||||
|
|
@ -154,11 +151,11 @@ private:
|
|||
AstNode* createSenseEquation(AstNodeSenItem* nodesp) {
|
||||
// Nodep may be a list of elements; we need to walk it
|
||||
AstNode* senEqnp = NULL;
|
||||
for (AstNodeSenItem* senp = nodesp; senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
for (AstNodeSenItem* senp = nodesp; senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) {
|
||||
AstNode* senOnep = NULL;
|
||||
if (AstSenItem* itemp = senp->castSenItem()) {
|
||||
if (AstSenItem* itemp = VN_CAST(senp, SenItem)) {
|
||||
senOnep = createSenItemEquation(itemp);
|
||||
} else if (AstSenGate* itemp = senp->castSenGate()) {
|
||||
} else if (AstSenGate* itemp = VN_CAST(senp, SenGate)) {
|
||||
senOnep = createSenGateEquation(itemp);
|
||||
} else {
|
||||
senp->v3fatalSrc("Strange node under sentree");
|
||||
|
|
@ -239,7 +236,7 @@ private:
|
|||
m_settleFuncp = funcp;
|
||||
}
|
||||
// Process the activates
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Done, clear so we can detect errors
|
||||
UINFO(4," TOPSCOPEDONE "<<nodep<<endl);
|
||||
clearLastSen();
|
||||
|
|
@ -249,13 +246,13 @@ private:
|
|||
virtual void visit(AstNodeModule* nodep) {
|
||||
//UINFO(4," MOD "<<nodep<<endl);
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp= NULL;
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
//UINFO(4," SCOPE "<<nodep<<endl);
|
||||
m_scopep = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (AstNode* movep = nodep->finalClksp()) {
|
||||
if (!m_topScopep) nodep->v3fatalSrc("Final clocks under non-top scope");
|
||||
movep->unlinkFrBackWithNext();
|
||||
|
|
@ -311,7 +308,7 @@ private:
|
|||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Link to global function
|
||||
if (nodep->formCallTree()) {
|
||||
UINFO(4, " formCallTree "<<nodep<<endl);
|
||||
|
|
@ -342,6 +339,30 @@ private:
|
|||
// Only empty blocks should be leftover on the non-top. Killem.
|
||||
if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active");
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else if (m_mtaskBodyp) {
|
||||
UINFO(4," TR ACTIVE "<<nodep<<endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (nodep->hasClocked()) {
|
||||
if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity");
|
||||
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
||||
UINFO(4," sameSenseTree\n");
|
||||
} else {
|
||||
clearLastSen();
|
||||
m_lastSenp = nodep->sensesp();
|
||||
// Make a new if statement
|
||||
m_lastIfp = makeActiveIf(m_lastSenp);
|
||||
m_mtaskBodyp->addStmtsp(m_lastIfp);
|
||||
}
|
||||
// Move statements to if
|
||||
m_lastIfp->addIfsp(stmtsp);
|
||||
} else if (nodep->hasInitial() || nodep->hasSettle()) {
|
||||
nodep->v3fatalSrc("MTask should not include initial/settle logic.");
|
||||
} else {
|
||||
// Combo logic. Move statements to mtask func.
|
||||
clearLastSen();
|
||||
m_mtaskBodyp->addStmtsp(stmtsp);
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else {
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
|
|
@ -376,11 +397,25 @@ private:
|
|||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstExecGraph* nodep) {
|
||||
for (m_mtaskBodyp = VN_CAST(nodep->op1p(), MTaskBody);
|
||||
m_mtaskBodyp;
|
||||
m_mtaskBodyp = VN_CAST(m_mtaskBodyp->nextp(), MTaskBody)) {
|
||||
clearLastSen();
|
||||
iterate(m_mtaskBodyp);
|
||||
}
|
||||
clearLastSen();
|
||||
// Move the ExecGraph into _eval. Its location marks the
|
||||
// spot where the graph will execute, relative to other
|
||||
// (serial) logic in the cycle.
|
||||
nodep->unlinkFrBack();
|
||||
addToEvalLoop(nodep);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -395,8 +430,9 @@ public:
|
|||
m_lastSenp = NULL;
|
||||
m_lastIfp = NULL;
|
||||
m_scopep = NULL;
|
||||
m_mtaskBodyp = NULL;
|
||||
//
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
// Allow downstream modules to find _eval()
|
||||
// easily without iterating through the tree.
|
||||
nodep->evalp(m_evalFuncp);
|
||||
|
|
|
|||
|
|
@ -60,13 +60,8 @@ protected:
|
|||
// STATE
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
|
||||
virtual ~CombBaseVisitor() {}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
//***** optimization levels
|
||||
static bool emptyFunctionDeletion() { return true; }
|
||||
|
|
@ -86,7 +81,7 @@ class CombCallVisitor : CombBaseVisitor {
|
|||
private:
|
||||
// NODE STATE
|
||||
bool m_find; // Find mode vs. delete mode
|
||||
typedef multimap<AstCFunc*,AstCCall*> CallMmap;
|
||||
typedef std::multimap<AstCFunc*,AstCCall*> CallMmap;
|
||||
CallMmap m_callMmap; // Associative array of {function}{call}
|
||||
// METHODS
|
||||
public:
|
||||
|
|
@ -95,7 +90,7 @@ public:
|
|||
if (newfuncp) {
|
||||
UINFO(4, " Replace "<<oldfuncp<<" -WITH-> "<<newfuncp<<endl);
|
||||
} else UINFO(4, " Remove "<<oldfuncp<<endl);
|
||||
pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(oldfuncp);
|
||||
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;
|
||||
|
|
@ -121,7 +116,7 @@ public:
|
|||
m_callMmap.insert(make_pair(nodep->funcp(), nodep));
|
||||
}
|
||||
void deleteCall(AstCCall* nodep) {
|
||||
pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(nodep->funcp());
|
||||
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;
|
||||
|
|
@ -141,7 +136,7 @@ private:
|
|||
virtual void visit(AstNodeAssign* nodep) {}
|
||||
virtual void visit(AstNodeMath* nodep) {}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -150,7 +145,7 @@ public:
|
|||
}
|
||||
virtual ~CombCallVisitor() {}
|
||||
void main(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -165,12 +160,12 @@ private:
|
|||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->user3(true);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CombMarkVisitor(AstNode* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CombMarkVisitor() {}
|
||||
};
|
||||
|
|
@ -214,18 +209,18 @@ private:
|
|||
CombineState oldState = m_state;
|
||||
{
|
||||
m_state = STATE_HASH;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
m_state = oldState;
|
||||
}
|
||||
void walkEmptyFuncs() {
|
||||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
AstNode* node1p = it->second;
|
||||
AstCFunc* oldfuncp = node1p->castCFunc();
|
||||
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
|
||||
if (oldfuncp
|
||||
&& oldfuncp->emptyBody()
|
||||
&& !oldfuncp->dontCombine()) {
|
||||
UINFO(5," EmptyFunc "<<hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
|
||||
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);
|
||||
|
|
@ -238,7 +233,7 @@ private:
|
|||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
V3Hash hashval = it->first;
|
||||
AstNode* node1p = it->second;
|
||||
if (!node1p->castCFunc()) continue;
|
||||
if (!VN_IS(node1p, CFunc)) continue;
|
||||
if (hashval.isIllegal()) node1p->v3fatalSrc("Illegal (unhashed) nodes");
|
||||
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
|
|
@ -247,16 +242,16 @@ private:
|
|||
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
|
||||
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
|
||||
// Replace AstCCall's that point here
|
||||
replaceFuncWFunc(node2p->castCFunc(), node1p->castCFunc());
|
||||
replaceFuncWFunc(VN_CAST(node2p, CFunc), VN_CAST(node1p, CFunc));
|
||||
// Replacement may promote a slow routine to fast path
|
||||
if (!node2p->castCFunc()->slow()) node1p->castCFunc()->slow(false);
|
||||
if (!VN_CAST(node2p, CFunc)->slow()) VN_CAST(node1p, CFunc)->slow(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
UINFO(5," DupFunc "<<hex<<V3Hash(newfuncp->user4p())<<" "<<newfuncp<<endl);
|
||||
UINFO(5," and "<<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);
|
||||
|
|
@ -265,7 +260,7 @@ private:
|
|||
pushDeletep(oldfuncp); VL_DANGLING(oldfuncp);
|
||||
}
|
||||
void replaceOnlyCallFunc(AstCCall* nodep) {
|
||||
if (AstCFunc* oldfuncp = nodep->backp()->castCFunc()) {
|
||||
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
|
||||
//oldfuncp->dumpTree(cout,"MAYDEL: ");
|
||||
if (nodep->nextp()==NULL
|
||||
&& oldfuncp->initsp()==NULL
|
||||
|
|
@ -288,7 +283,7 @@ private:
|
|||
AstNode* bestLast1p = NULL;
|
||||
AstNode* bestLast2p = NULL;
|
||||
//
|
||||
pair <V3Hashed::iterator,V3Hashed::iterator> eqrange = m_hashed.mmap().equal_range(hashval);
|
||||
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;
|
||||
|
|
@ -396,7 +391,7 @@ private:
|
|||
//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.
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
iterateChildrenBackwards(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
|
|
@ -406,7 +401,7 @@ private:
|
|||
m_hashed.clear();
|
||||
// Compute hash of all statement trees in the function
|
||||
m_state = STATE_HASH;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_state = STATE_IDLE;
|
||||
if (debug()>=9) {
|
||||
m_hashed.dumpFilePrefixed("combine");
|
||||
|
|
@ -422,7 +417,7 @@ private:
|
|||
// Walk the statements looking for large replicated code sections
|
||||
if (statementCombine()) {
|
||||
m_state = STATE_DUP;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_state = STATE_IDLE;
|
||||
}
|
||||
m_modp = NULL;
|
||||
|
|
@ -433,7 +428,7 @@ private:
|
|||
if (m_state == STATE_HASH) {
|
||||
hashStatement(nodep); // Hash the entire function - it might be identical
|
||||
} else if (m_state == STATE_DUP) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
m_funcp = NULL;
|
||||
|
|
@ -453,16 +448,19 @@ private:
|
|||
virtual void visit(AstTraceDecl*) {}
|
||||
virtual void visit(AstTraceInc*) {}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CombineVisitor(AstNetlist* nodep) {
|
||||
m_modp=NULL;
|
||||
m_funcp = NULL;
|
||||
m_state = STATE_IDLE;
|
||||
nodep->accept(*this);
|
||||
m_modp = NULL;
|
||||
m_funcp = NULL;
|
||||
m_modNFuncs = 0;
|
||||
m_walkLast1p = NULL;
|
||||
m_walkLast2p = NULL;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CombineVisitor() {
|
||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@ public:
|
|||
return (m_on>rh.m_on);
|
||||
}
|
||||
};
|
||||
ostream& operator<<(ostream& os, V3ConfigLine rhs) { return os<<rhs.m_lineno<<", "<<rhs.m_code<<", "<<rhs.m_on; }
|
||||
std::ostream& operator<<(std::ostream& os, V3ConfigLine rhs) { return os<<rhs.m_lineno<<", "<<rhs.m_code<<", "<<rhs.m_on; }
|
||||
|
||||
class V3ConfigIgnores {
|
||||
typedef multiset<V3ConfigLine> IgnLines; // list of {line,code,on}
|
||||
typedef map<string,IgnLines> IgnFiles; // {filename} => list of {line,code,on}
|
||||
typedef std::multiset<V3ConfigLine> IgnLines; // list of {line,code,on}
|
||||
typedef std::map<string,IgnLines> IgnFiles; // {filename} => list of {line,code,on}
|
||||
|
||||
// MEMBERS
|
||||
string m_lastFilename; // Last filename looked up
|
||||
|
|
|
|||
699
src/V3Const.cpp
699
src/V3Const.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -45,7 +45,7 @@
|
|||
class CoverageVisitor : public AstNVisitor {
|
||||
private:
|
||||
// TYPES
|
||||
typedef map<string,int> FileMap;
|
||||
typedef std::map<string,int> FileMap;
|
||||
|
||||
struct ToggleEnt {
|
||||
string m_comment; // Comment for coverage dump
|
||||
|
|
@ -74,11 +74,7 @@ private:
|
|||
string m_beginHier; // AstBegin hier name for user coverage points
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
const char* varIgnoreToggle(AstVar* nodep) {
|
||||
// Return true if this shouldn't be traced
|
||||
|
|
@ -132,7 +128,7 @@ private:
|
|||
m_modp = nodep;
|
||||
m_inModOff = nodep->isTop(); // Ignore coverage on top module; it's a shell we created
|
||||
m_fileps.clear();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
m_inModOff = true;
|
||||
}
|
||||
|
|
@ -142,12 +138,12 @@ private:
|
|||
bool oldtog = m_inToggleOff;
|
||||
{
|
||||
m_inToggleOff = true;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_inToggleOff = oldtog;
|
||||
}
|
||||
virtual void visit(AstVar* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (m_modp && !m_inModOff && !m_inToggleOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageToggle()) {
|
||||
const char* disablep = varIgnoreToggle(nodep);
|
||||
|
|
@ -200,7 +196,7 @@ private:
|
|||
void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration
|
||||
const ToggleEnt& above,
|
||||
AstVar* varp, AstVar* chgVarp) { // Constant
|
||||
if (AstBasicDType* bdtypep = dtypep->castBasicDType()) {
|
||||
if (const AstBasicDType* bdtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
if (bdtypep->isRanged()) {
|
||||
for (int index_docs=bdtypep->lsb(); index_docs<bdtypep->msb()+1; index_docs++) {
|
||||
int index_code = index_docs - bdtypep->lsb();
|
||||
|
|
@ -218,7 +214,7 @@ private:
|
|||
varp, chgVarp);
|
||||
}
|
||||
}
|
||||
else if (AstUnpackArrayDType* adtypep = dtypep->castUnpackArrayDType()) {
|
||||
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)+"]",
|
||||
|
|
@ -230,7 +226,7 @@ private:
|
|||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else if (AstPackArrayDType* adtypep = dtypep->castPackArrayDType()) {
|
||||
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();
|
||||
|
|
@ -245,9 +241,9 @@ private:
|
|||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else if (AstStructDType* adtypep = dtypep->castStructDType()) {
|
||||
else if (AstStructDType* adtypep = VN_CAST(dtypep, StructDType)) {
|
||||
// For now it's packed, so similar to array
|
||||
for (AstMemberDType* itemp = adtypep->membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) {
|
||||
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(),
|
||||
|
|
@ -261,7 +257,7 @@ private:
|
|||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else if (AstUnionDType* adtypep = dtypep->castUnionDType()) {
|
||||
else if (AstUnionDType* adtypep = VN_CAST(dtypep, UnionDType)) {
|
||||
// Arbitrarially handle only the first member of the union
|
||||
if (AstMemberDType* itemp = adtypep->membersp()) {
|
||||
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
|
||||
|
|
@ -284,11 +280,11 @@ private:
|
|||
UINFO(4," IF: "<<nodep<<endl);
|
||||
if (m_checkBlock) {
|
||||
// An else-if. When we iterate the if, use "elsif" marking
|
||||
bool elsif = (nodep->elsesp()->castIf()
|
||||
&& !nodep->elsesp()->castIf()->nextp());
|
||||
if (elsif) nodep->elsesp()->castIf()->user1(true);
|
||||
bool elsif = (VN_IS(nodep->elsesp(), If)
|
||||
&& !VN_CAST(nodep->elsesp(), If)->nextp());
|
||||
if (elsif) VN_CAST(nodep->elsesp(), If)->user1(true);
|
||||
//
|
||||
nodep->ifsp()->iterateAndNext(*this);
|
||||
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);
|
||||
|
|
@ -301,7 +297,7 @@ private:
|
|||
// Don't do empty else's, only empty if/case's
|
||||
if (nodep->elsesp()) {
|
||||
m_checkBlock = true;
|
||||
nodep->elsesp()->iterateAndNext(*this);
|
||||
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);
|
||||
|
|
@ -317,7 +313,7 @@ private:
|
|||
UINFO(4," CASEI: "<<nodep<<endl);
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) {
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
if (m_checkBlock) { // if the case body didn't disable it
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
nodep->addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case"));
|
||||
|
|
@ -328,7 +324,7 @@ private:
|
|||
virtual void visit(AstPslCover* nodep) {
|
||||
UINFO(4," PSLCOVER: "<<nodep<<endl);
|
||||
m_checkBlock = true; // Always do cover blocks, even if there's a $stop
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->coverincp()) {
|
||||
// Note the name may be overridden by V3Assert processing
|
||||
nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover"));
|
||||
|
|
@ -346,7 +342,7 @@ private:
|
|||
m_checkBlock = false;
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
} else {
|
||||
if (m_checkBlock) nodep->iterateChildren(*this);
|
||||
if (m_checkBlock) iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) {
|
||||
|
|
@ -362,7 +358,7 @@ private:
|
|||
if (nodep->name()!="") {
|
||||
m_beginHier = m_beginHier + (m_beginHier!=""?".":"") + nodep->name();
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_beginHier = oldHier;
|
||||
m_inToggleOff = oldtog;
|
||||
|
|
@ -372,7 +368,7 @@ private:
|
|||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
if (m_checkBlock) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_checkBlock = true; // Reset as a child may have cleared it
|
||||
}
|
||||
}
|
||||
|
|
@ -386,7 +382,7 @@ public:
|
|||
m_beginHier = "";
|
||||
m_inToggleOff = false;
|
||||
m_inModOff = true;
|
||||
rootp->iterateChildren(*this);
|
||||
iterateChildren(rootp);
|
||||
}
|
||||
virtual ~CoverageVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ private:
|
|||
//AstUser4InUse In V3Hashed
|
||||
|
||||
// TYPES
|
||||
typedef vector<AstCoverToggle*> ToggleList;
|
||||
typedef std::vector<AstCoverToggle*> ToggleList;
|
||||
|
||||
// STATE
|
||||
ToggleList m_toggleps; // List of of all AstCoverToggle's
|
||||
|
|
@ -54,11 +54,7 @@ private:
|
|||
V3Double0 m_statToggleJoins; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void detectDuplicates() {
|
||||
UINFO(9,"Finding duplicates\n");
|
||||
|
|
@ -82,7 +78,7 @@ private:
|
|||
AstNode* duporigp = hashed.iteratorNodep(dupit);
|
||||
// Note hashed will point to the original variable (what's duplicated), not the covertoggle,
|
||||
// but we need to get back to the covertoggle which is immediately above, so:
|
||||
AstCoverToggle* removep = duporigp->backp()->castCoverToggle();
|
||||
AstCoverToggle* removep = VN_CAST(duporigp->backp(), CoverToggle);
|
||||
if (!removep) nodep->v3fatalSrc("CoverageJoin duplicate of wrong type");
|
||||
UINFO(8," Orig "<<nodep<<" -->> "<<nodep->incp()->declp()<<endl);
|
||||
UINFO(8," dup "<<removep<<" -->> "<<removep->incp()->declp()<<endl);
|
||||
|
|
@ -105,24 +101,24 @@ private:
|
|||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
// Find all Coverage's
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Simplify
|
||||
detectDuplicates();
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) {
|
||||
m_toggleps.push_back(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNodeMath* nodep) {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit CoverageJoinVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CoverageJoinVisitor() {
|
||||
V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins);
|
||||
|
|
|
|||
|
|
@ -59,18 +59,18 @@ private:
|
|||
// ** Shared with DeadVisitor **
|
||||
// VISITORS
|
||||
virtual void visit(AstCell* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->modp()->user1Inc(-1);
|
||||
}
|
||||
//-----
|
||||
virtual void visit(AstNodeMath* nodep) {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit DeadModVisitor(AstNodeModule* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~DeadModVisitor() {}
|
||||
};
|
||||
|
|
@ -89,15 +89,15 @@ private:
|
|||
AstUser1InUse m_inuser1;
|
||||
|
||||
// TYPES
|
||||
typedef multimap<AstVarScope*,AstNodeAssign*> AssignMap;
|
||||
typedef std::multimap<AstVarScope*,AstNodeAssign*> AssignMap;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
vector<AstVar*> m_varsp; // List of all encountered to avoid another loop through tree
|
||||
vector<AstNode*> m_dtypesp; // List of all encountered to avoid another loop through tree
|
||||
vector<AstVarScope*> m_vscsp; // List of all encountered to avoid another loop through tree
|
||||
vector<AstScope*> m_scopesp; // List of all encountered to avoid another loop through tree
|
||||
vector<AstCell*> m_cellsp; // List of all encountered to avoid another loop through tree
|
||||
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
|
||||
|
|
@ -106,11 +106,7 @@ private:
|
|||
bool m_sideEffect; // Side effects discovered in assign RHS
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void checkAll(AstNode* nodep) {
|
||||
if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves
|
||||
|
|
@ -125,7 +121,7 @@ private:
|
|||
void checkDType(AstNodeDType* nodep) {
|
||||
if (!nodep->generic() // Don't remove generic types
|
||||
&& m_elimDTypes // dtypes stick around until post-widthing
|
||||
&& !nodep->castMemberDType() // Keep member names iff upper type exists
|
||||
&& !VN_IS(nodep, MemberDType) // Keep member names iff upper type exists
|
||||
) {
|
||||
m_dtypesp.push_back(nodep);
|
||||
}
|
||||
|
|
@ -138,18 +134,18 @@ private:
|
|||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
if (!nodep->dead()) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
}
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc();
|
||||
|
||||
|
|
@ -158,14 +154,14 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
m_cellsp.push_back(nodep);
|
||||
nodep->modp()->user1Inc();
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeVarRef* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->varScopep()) {
|
||||
nodep->varScopep()->user1Inc();
|
||||
|
|
@ -180,7 +176,7 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
|
|
@ -188,7 +184,7 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstRefDType* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkDType(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
|
|
@ -197,12 +193,12 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkDType(nodep);
|
||||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstEnumItemRef* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
|
|
@ -211,7 +207,7 @@ private:
|
|||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstModport* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (m_elimCells) {
|
||||
if (!nodep->varsp()) {
|
||||
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
|
||||
|
|
@ -221,7 +217,7 @@ private:
|
|||
checkAll(nodep);
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (m_elimCells && !nodep->attrPublic()) {
|
||||
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
|
||||
return;
|
||||
|
|
@ -229,10 +225,10 @@ private:
|
|||
checkAll(nodep);
|
||||
// Don't let packages with only public variables disappear
|
||||
// Normal modules may disappear, e.g. if they are parameterized then removed
|
||||
if (nodep->attrPublic() && m_modp && m_modp->castPackage()) m_modp->user1Inc();
|
||||
if (nodep->attrPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
||||
if (mightElimVar(nodep->varp())) {
|
||||
|
|
@ -240,9 +236,9 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstVar* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->isSigPublic() && m_modp && m_modp->castPackage()) m_modp->user1Inc();
|
||||
if (nodep->isSigPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
|
||||
if (mightElimVar(nodep)) {
|
||||
m_varsp.push_back(nodep);
|
||||
}
|
||||
|
|
@ -251,16 +247,16 @@ private:
|
|||
// See if simple assignments to variables may be eliminated because that variable is never used.
|
||||
// Similar code in V3Life
|
||||
m_sideEffect = false;
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
checkAll(nodep);
|
||||
// Has to be direct assignment without any EXTRACTing.
|
||||
AstVarRef* varrefp = nodep->lhsp()->castVarRef();
|
||||
AstVarRef* varrefp = VN_CAST(nodep->lhsp(), VarRef);
|
||||
if (varrefp && !m_sideEffect
|
||||
&& varrefp->varScopep()) { // For simplicity, we only remove post-scoping
|
||||
m_assignMap.insert(make_pair(varrefp->varScopep(), nodep));
|
||||
checkAll(varrefp); // Must track reference to dtype()
|
||||
} else { // Track like any other statement
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
}
|
||||
checkAll(nodep);
|
||||
}
|
||||
|
|
@ -268,7 +264,7 @@ private:
|
|||
//-----
|
||||
virtual void visit(AstNode* nodep) {
|
||||
if (nodep->isOutputter()) m_sideEffect=true;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
}
|
||||
|
||||
|
|
@ -281,7 +277,7 @@ private:
|
|||
retry=false;
|
||||
AstNodeModule* nextmodp;
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
|
||||
nextmodp = modp->nextp()->castNodeModule();
|
||||
nextmodp = VN_CAST(modp->nextp(), NodeModule);
|
||||
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);
|
||||
|
|
@ -307,7 +303,7 @@ private:
|
|||
void deadCheckScope() {
|
||||
for (bool retry=true; retry; ) {
|
||||
retry = false;
|
||||
for (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;
|
||||
|
|
@ -326,7 +322,7 @@ private:
|
|||
}
|
||||
|
||||
void deadCheckCells() {
|
||||
for (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);
|
||||
|
|
@ -337,11 +333,11 @@ private:
|
|||
|
||||
void deadCheckVar() {
|
||||
// Delete any unused varscopes
|
||||
for (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);
|
||||
pair <AssignMap::iterator,AssignMap::iterator> eqrange = m_assignMap.equal_range(vscp);
|
||||
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);
|
||||
|
|
@ -355,7 +351,7 @@ private:
|
|||
}
|
||||
for (bool retry=true; retry; ) {
|
||||
retry = false;
|
||||
for (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;
|
||||
|
|
@ -370,15 +366,16 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
for (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) {
|
||||
AstNodeClassDType *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 = (*it)->castNodeClassDType())) {
|
||||
if ((classp = VN_CAST((*it), NodeClassDType))) {
|
||||
bool cont = true;
|
||||
for (AstMemberDType *memberp = classp->membersp(); memberp; memberp = memberp->nextp()->castMemberDType()) {
|
||||
for (AstMemberDType *memberp = classp->membersp();
|
||||
memberp; memberp = VN_CAST(memberp->nextp(), MemberDType)) {
|
||||
if (memberp->user1() != 0) {
|
||||
cont = false;
|
||||
break;
|
||||
|
|
@ -404,7 +401,7 @@ public:
|
|||
// Prepare to remove some datatypes
|
||||
nodep->typeTablep()->clearCache();
|
||||
// Operate on whole netlist
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
|
||||
deadCheckVar();
|
||||
// We only elimate scopes when in a flattened structure
|
||||
|
|
|
|||
|
|
@ -98,18 +98,14 @@ private:
|
|||
bool m_inDly; // True in delayed assignments
|
||||
bool m_inLoop; // True in for loops
|
||||
bool m_inInitial; // True in intial blocks
|
||||
typedef std::map<pair<AstNodeModule*,string>,AstVar*> VarMap;
|
||||
typedef std::map<std::pair<AstNodeModule*,string>,AstVar*> VarMap;
|
||||
VarMap m_modVarMap; // Table of new var names created under module
|
||||
V3Double0 m_statSharedSet;// Statistic tracking
|
||||
typedef std::map<AstVarScope*,int> ScopeVecMap;
|
||||
ScopeVecMap m_scopeVecMap; // Next var number for each scope
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void markVarUsage(AstVarScope* nodep, uint32_t flags) {
|
||||
//UINFO(4," MVU "<<flags<<" "<<nodep<<endl);
|
||||
|
|
@ -192,36 +188,36 @@ private:
|
|||
AstNode* newlhsp = NULL; // NULL = unlink old assign
|
||||
AstSel* bitselp = NULL;
|
||||
AstArraySel* arrayselp = NULL;
|
||||
if (lhsp->castSel()) {
|
||||
bitselp = lhsp->castSel();
|
||||
arrayselp = bitselp->fromp()->castArraySel();
|
||||
if (VN_IS(lhsp, Sel)) {
|
||||
bitselp = VN_CAST(lhsp, Sel);
|
||||
arrayselp = VN_CAST(bitselp->fromp(), ArraySel);
|
||||
} else {
|
||||
arrayselp = lhsp->castArraySel();
|
||||
arrayselp = VN_CAST(lhsp, ArraySel);
|
||||
}
|
||||
if (!arrayselp) nodep->v3fatalSrc("No arraysel under bitsel?");
|
||||
if (arrayselp->dtypep()->skipRefp()->castUnpackArrayDType()) {
|
||||
if (VN_IS(arrayselp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
nodep->v3fatalSrc("ArraySel with unpacked arrays should have been removed in V3Slice");
|
||||
}
|
||||
UINFO(4,"AssignDlyArray: "<<nodep<<endl);
|
||||
//
|
||||
//=== Dimensions: __Vdlyvdim__
|
||||
deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment
|
||||
std::deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment
|
||||
AstNode* dimselp=arrayselp;
|
||||
for (; dimselp->castArraySel(); dimselp=dimselp->castArraySel()->fromp()) {
|
||||
AstNode* valp = dimselp->castArraySel()->bitp()->unlinkFrBack();
|
||||
for (; VN_IS(dimselp, ArraySel); dimselp=VN_CAST(dimselp, ArraySel)->fromp()) {
|
||||
AstNode* valp = VN_CAST(dimselp, ArraySel)->bitp()->unlinkFrBack();
|
||||
dimvalp.push_front(valp);
|
||||
}
|
||||
AstVarRef* varrefp = dimselp->castVarRef();
|
||||
AstVarRef* varrefp = VN_CAST(dimselp, VarRef);
|
||||
if (!varrefp) nodep->v3fatalSrc("No var underneath arraysels");
|
||||
if (!varrefp->varScopep()) varrefp->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
||||
varrefp->unlinkFrBack();
|
||||
AstVar* oldvarp = varrefp->varp();
|
||||
int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
|
||||
//
|
||||
deque<AstNode*> dimreadps; // Read value for each dimension of assignment
|
||||
std::deque<AstNode*> dimreadps; // Read value for each dimension of assignment
|
||||
for (unsigned dimension=0; dimension<dimvalp.size(); dimension++) {
|
||||
AstNode* dimp = dimvalp[dimension];
|
||||
if (dimp->castConst()) { // bit = const, can just use it
|
||||
if (VN_IS(dimp, Const)) { // bit = const, can just use it
|
||||
dimreadps.push_front(dimp);
|
||||
} else {
|
||||
string bitvarname = (string("__Vdlyvdim")+cvtToStr(dimension)
|
||||
|
|
@ -240,7 +236,7 @@ private:
|
|||
AstNode* bitreadp=NULL; // Code to read Vdlyvlsb
|
||||
if (bitselp) {
|
||||
AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack();
|
||||
if (bitselp->fromp()->castConst()) {// 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));
|
||||
|
|
@ -255,7 +251,7 @@ private:
|
|||
//
|
||||
//=== Value: __Vdlyvval__
|
||||
AstNode* valreadp; // Code to read Vdlyvval
|
||||
if (nodep->rhsp()->castConst()) { // 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));
|
||||
|
|
@ -273,7 +269,7 @@ private:
|
|||
// then we told this nodep->user3 we can use its Vdlyvset rather than making a new one.
|
||||
// This is good for code like:
|
||||
// for (i=0; i<5; i++) vector[i] <= something;
|
||||
setvscp = nodep->user3p()->castVarScope();
|
||||
setvscp = VN_CAST(nodep->user3p(), VarScope);
|
||||
++m_statSharedSet;
|
||||
} else { // Create new one
|
||||
string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
|
|
@ -308,9 +304,9 @@ private:
|
|||
// Build "IF (changeit) ...
|
||||
UINFO(9," For "<<setvscp<<endl);
|
||||
UINFO(9," & "<<varrefp<<endl);
|
||||
AstAlwaysPost* finalp = varrefp->varScopep()->user4p()->castAlwaysPost();
|
||||
AstAlwaysPost* finalp = VN_CAST(varrefp->varScopep()->user4p(), AlwaysPost);
|
||||
if (finalp) {
|
||||
AstActive* oldactivep = finalp->user2p()->castActive();
|
||||
AstActive* oldactivep = VN_CAST(finalp->user2p(), Active);
|
||||
checkActivePost(varrefp, oldactivep);
|
||||
if (setinitp) oldactivep->addStmtsp(setinitp);
|
||||
} else { // first time we've dealt with this memory
|
||||
|
|
@ -326,7 +322,7 @@ private:
|
|||
if (finalp->user3p() == setvscp) {
|
||||
// Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*,
|
||||
// we can share the IF statement too
|
||||
postLogicp = finalp->user4p()->castIf();
|
||||
postLogicp = VN_CAST(finalp->user4p(), If);
|
||||
if (!postLogicp) nodep->v3fatalSrc("Delayed assignment misoptimized; prev var found w/o associated IF");
|
||||
} else {
|
||||
postLogicp = new AstIf (nodep->fileline(),
|
||||
|
|
@ -346,16 +342,16 @@ private:
|
|||
virtual void visit(AstNetlist* nodep) {
|
||||
//VV***** We reset all userp() on the netlist
|
||||
m_modVarMap.clear();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
AstNode::user3ClearTree();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
m_cfuncp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_cfuncp = NULL;
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
|
|
@ -363,16 +359,16 @@ private:
|
|||
bool oldinit = m_inInitial;
|
||||
m_inInitial = nodep->hasInitial();
|
||||
AstNode::user3ClearTree(); // Two sets to same variable in different actives must use different vars.
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_inInitial = oldinit;
|
||||
}
|
||||
virtual void visit(AstAssignDly* nodep) {
|
||||
m_inDly = true;
|
||||
m_nextDlyp = nodep->nextp()->castAssignDly(); // Next assignment in same block, maybe NULL.
|
||||
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 (nodep->lhsp()->castArraySel()
|
||||
|| (nodep->lhsp()->castSel()
|
||||
&& nodep->lhsp()->castSel()->fromp()->castArraySel())) {
|
||||
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)");
|
||||
|
|
@ -384,7 +380,7 @@ private:
|
|||
lhsp->deleteTree(); VL_DANGLING(lhsp);
|
||||
}
|
||||
else {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_inDly = false;
|
||||
m_nextDlyp = NULL;
|
||||
|
|
@ -401,9 +397,9 @@ private:
|
|||
}
|
||||
AstVarScope* oldvscp = nodep->varScopep();
|
||||
if (!oldvscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp");
|
||||
AstVarScope* dlyvscp = oldvscp->user1p()->castVarScope();
|
||||
AstVarScope* dlyvscp = VN_CAST(oldvscp->user1p(), VarScope);
|
||||
if (dlyvscp) { // Multiple use of delayed variable
|
||||
AstActive* oldactivep = dlyvscp->user2p()->castActive();
|
||||
AstActive* oldactivep = VN_CAST(dlyvscp->user2p(), Active);
|
||||
checkActivePost(nodep, oldactivep);
|
||||
}
|
||||
if (!dlyvscp) { // First use of this delayed variable
|
||||
|
|
@ -445,14 +441,14 @@ private:
|
|||
virtual void visit(AstWhile* nodep) {
|
||||
bool oldloop = m_inLoop;
|
||||
m_inLoop = true;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_inLoop = oldloop;
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -465,7 +461,7 @@ public:
|
|||
m_inLoop = false;
|
||||
m_inInitial = false;
|
||||
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~DelayedVisitor() {
|
||||
V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet);
|
||||
|
|
|
|||
|
|
@ -52,11 +52,7 @@ private:
|
|||
int m_maxdepth; // Maximum depth in an expression
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void createDeepTemp(AstNode* nodep) {
|
||||
UINFO(6," Deep "<<nodep<<endl);
|
||||
|
|
@ -89,21 +85,21 @@ private:
|
|||
UINFO(4," MOD "<<nodep<<endl);
|
||||
m_modp = nodep;
|
||||
m_funcp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
m_funcp = nodep;
|
||||
m_depth = 0;
|
||||
m_maxdepth = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_funcp = NULL;
|
||||
}
|
||||
void visitStmt(AstNodeStmt* nodep) {
|
||||
m_depth = 0;
|
||||
m_maxdepth = 0;
|
||||
m_stmtp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_stmtp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeStmt* nodep) {
|
||||
|
|
@ -116,13 +112,13 @@ private:
|
|||
// We have some operator defines that use 2 parens, so += 2.
|
||||
m_depth += 2;
|
||||
if (m_depth>m_maxdepth) m_maxdepth=m_depth;
|
||||
nodep->iterateChildren(*this);
|
||||
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()
|
||||
&& !nodep->backp()->castNodeStmt() // Not much point if we're about to use it
|
||||
&& !VN_IS(nodep->backp(), NodeStmt) // Not much point if we're about to use it
|
||||
) {
|
||||
m_maxdepth = m_depth;
|
||||
createDeepTemp(nodep);
|
||||
|
|
@ -141,7 +137,7 @@ private:
|
|||
}
|
||||
virtual void visit(AstUCFunc* nodep) {
|
||||
needNonStaticFunc(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstUCStmt* nodep) {
|
||||
needNonStaticFunc(nodep);
|
||||
|
|
@ -152,7 +148,7 @@ private:
|
|||
// Default: Just iterate
|
||||
virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -164,7 +160,7 @@ public:
|
|||
m_depth=0;
|
||||
m_maxdepth=0;
|
||||
//
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~DepthVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -49,11 +49,7 @@ private:
|
|||
int m_deepNum; // How many functions made
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
AstCFunc* createDeepFunc(AstNode* nodep) {
|
||||
AstNRelinker relinkHandle;
|
||||
|
|
@ -80,7 +76,7 @@ private:
|
|||
UINFO(4," MOD "<<nodep<<endl);
|
||||
m_modp = nodep;
|
||||
m_deepNum = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
|
|
@ -90,7 +86,7 @@ private:
|
|||
{
|
||||
m_depth = 0;
|
||||
m_funcp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_depth = lastDepth;
|
||||
m_funcp = lastFuncp;
|
||||
|
|
@ -98,16 +94,16 @@ private:
|
|||
void visitStmt(AstNodeStmt* nodep) {
|
||||
m_depth++;
|
||||
if (m_depth > v3Global.opt.compLimitBlocks()
|
||||
&& !nodep->castCCall()) { // Already done
|
||||
&& !VN_IS(nodep, CCall)) { // Already done
|
||||
UINFO(4, "DeepBlocks "<<m_depth<<" "<<nodep<<endl);
|
||||
AstNode* backp = nodep->backp(); // Only for debug
|
||||
if (debug()>=9) backp->dumpTree(cout,"- pre : ");
|
||||
AstCFunc* funcp = createDeepFunc(nodep);
|
||||
funcp->accept(*this);
|
||||
iterate(funcp);
|
||||
if (debug()>=9) backp->dumpTree(cout,"- post: ");
|
||||
if (debug()>=9) funcp->dumpTree(cout,"- func: ");
|
||||
} else {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_depth--;
|
||||
}
|
||||
|
|
@ -120,7 +116,7 @@ private:
|
|||
// Default: Just iterate
|
||||
virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -131,7 +127,7 @@ public:
|
|||
m_depth = 0;
|
||||
m_deepNum = 0;
|
||||
//
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~DepthBlockVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ private:
|
|||
AstUser1InUse m_inuser1;
|
||||
|
||||
// TYPES
|
||||
typedef multimap<string,AstCFunc*> FuncMmap;
|
||||
typedef std::multimap<string,AstCFunc*> FuncMmap;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
|
|
@ -58,17 +58,13 @@ private:
|
|||
FuncMmap m_modFuncs; // Name of public functions added
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
static bool modIsSingleton(AstNodeModule* modp) {
|
||||
// True iff there's exactly one instance of this module in the design.
|
||||
int instances = 0;
|
||||
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
|
||||
if (stmtp->castScope()) {
|
||||
if (VN_IS(stmtp, Scope)) {
|
||||
if (++instances > 1) { return false; }
|
||||
}
|
||||
}
|
||||
|
|
@ -187,7 +183,7 @@ private:
|
|||
funcp->declPrivate(true);
|
||||
AstNode* argsp = NULL;
|
||||
for (AstNode* stmtp = newfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) {
|
||||
if (AstVar* portp = stmtp->castVar()) {
|
||||
if (AstVar* portp = VN_CAST(stmtp, Var)) {
|
||||
if (portp->isIO() && !portp->isFuncReturn()) {
|
||||
AstNode* newp = new AstVarRef(portp->fileline(), portp, portp->isOutput());
|
||||
if (argsp) argsp = argsp->addNextNull(newp);
|
||||
|
|
@ -234,13 +230,13 @@ private:
|
|||
m_modp = nodep;
|
||||
m_modFuncs.clear();
|
||||
m_modSingleton = modIsSingleton(m_modp);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
makePublicFuncWrappers();
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
m_scopep = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_scopep = NULL;
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep) {
|
||||
|
|
@ -249,7 +245,7 @@ private:
|
|||
pushDeletep(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeVarRef* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Convert the hierch name
|
||||
if (!m_scopep) nodep->v3fatalSrc("Node not under scope");
|
||||
bool hierThis;
|
||||
|
|
@ -259,7 +255,7 @@ private:
|
|||
}
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
//UINFO(9," "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Convert the hierch name
|
||||
if (!m_scopep) nodep->v3fatalSrc("Node not under scope");
|
||||
if (!nodep->funcp()->scopep()) nodep->v3fatalSrc("CFunc not under scope");
|
||||
|
|
@ -271,7 +267,7 @@ private:
|
|||
virtual void visit(AstCFunc* nodep) {
|
||||
if (!nodep->user1()) {
|
||||
m_needThis = false;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(true);
|
||||
if (m_needThis) {
|
||||
nodep->isStatic(false);
|
||||
|
|
@ -292,7 +288,7 @@ private:
|
|||
}
|
||||
virtual void visit(AstVar*) {}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -301,7 +297,7 @@ public:
|
|||
m_scopep(NULL),
|
||||
m_modSingleton(false),
|
||||
m_needThis(false) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~DescopeVisitor() {}
|
||||
};
|
||||
|
|
|
|||
839
src/V3EmitC.cpp
839
src/V3EmitC.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -66,12 +66,12 @@ public:
|
|||
v3Global.rootp()->addFilesp(cfilep);
|
||||
return cfilep;
|
||||
}
|
||||
string cFuncArgs(AstCFunc* nodep) {
|
||||
string cFuncArgs(const AstCFunc* nodep) {
|
||||
// Return argument list for given C function
|
||||
string args = nodep->argTypes();
|
||||
// Might be a user function with argument list.
|
||||
for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) {
|
||||
if (AstVar* portp = stmtp->castVar()) {
|
||||
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())
|
||||
|
|
@ -102,13 +102,13 @@ private:
|
|||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) {
|
||||
m_count++;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit EmitCBaseCounterVisitor(AstNode* nodep) {
|
||||
m_count = 0;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~EmitCBaseCounterVisitor() {}
|
||||
int count() const { return m_count; }
|
||||
|
|
|
|||
|
|
@ -51,13 +51,13 @@ class EmitCInlines : EmitCBaseVisitor {
|
|||
virtual void visit(AstNodeStmt*) {}
|
||||
// Default
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//---------------------------------------
|
||||
// ACCESSORS
|
||||
public:
|
||||
explicit EmitCInlines(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
if (v3Global.needHInlines()) {
|
||||
emitInt();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,11 +57,11 @@ class EmitCSyms : EmitCBaseVisitor {
|
|||
ScopeVarData(const string& scopeName, const string& varBasePretty, AstVar* varp, AstNodeModule* modp, AstScope* scopep)
|
||||
: m_scopeName(scopeName), m_varBasePretty(varBasePretty), m_varp(varp), m_modp(modp), m_scopep(scopep) {}
|
||||
};
|
||||
typedef map<string,ScopeFuncData> ScopeFuncs;
|
||||
typedef map<string,ScopeVarData> ScopeVars;
|
||||
typedef map<string,ScopeNameData> ScopeNames;
|
||||
typedef pair<AstScope*,AstNodeModule*> ScopeModPair;
|
||||
typedef pair<AstNodeModule*,AstVar*> ModVarPair;
|
||||
typedef std::map<string,ScopeFuncData> ScopeFuncs;
|
||||
typedef std::map<string,ScopeVarData> ScopeVars;
|
||||
typedef std::map<string,ScopeNameData> ScopeNames;
|
||||
typedef std::pair<AstScope*,AstNodeModule*> ScopeModPair;
|
||||
typedef std::pair<AstNodeModule*,AstVar*> ModVarPair;
|
||||
struct CmpName {
|
||||
inline bool operator () (const ScopeModPair& lhsp, const ScopeModPair& rhsp) const {
|
||||
return lhsp.first->name() < rhsp.first->name();
|
||||
|
|
@ -80,9 +80,9 @@ class EmitCSyms : EmitCBaseVisitor {
|
|||
// STATE
|
||||
AstCFunc* m_funcp; // Current function
|
||||
AstNodeModule* m_modp; // Current module
|
||||
vector<ScopeModPair> m_scopes; // Every scope by module
|
||||
vector<AstCFunc*> m_dpis; // DPI functions
|
||||
vector<ModVarPair> m_modVars; // Each public {mod,var}
|
||||
std::vector<ScopeModPair> m_scopes; // Every scope by module
|
||||
std::vector<AstCFunc*> m_dpis; // DPI functions
|
||||
std::vector<ModVarPair> m_modVars; // Each public {mod,var}
|
||||
ScopeNames m_scopeNames; // Each unique AstScopeName
|
||||
ScopeFuncs m_scopeFuncs; // Each {scope,dpi-export-func}
|
||||
ScopeVars m_scopeVars; // Each {scope,public-var}
|
||||
|
|
@ -113,9 +113,9 @@ class EmitCSyms : EmitCBaseVisitor {
|
|||
// We didn'e have all m_scopes loaded when we encountered variables, so expand them now
|
||||
// It would be less code if each module inserted its own variables.
|
||||
// Someday. For now public isn't common.
|
||||
for (vector<ScopeModPair>::iterator itsc = m_scopes.begin(); itsc != m_scopes.end(); ++itsc) {
|
||||
for (std::vector<ScopeModPair>::iterator itsc = m_scopes.begin(); itsc != m_scopes.end(); ++itsc) {
|
||||
AstScope* scopep = itsc->first; AstNodeModule* smodp = itsc->second;
|
||||
for (vector<ModVarPair>::iterator it = m_modVars.begin(); it != m_modVars.end(); ++it) {
|
||||
for (std::vector<ModVarPair>::iterator it = m_modVars.begin(); it != m_modVars.end(); ++it) {
|
||||
AstNodeModule* modp = it->first;
|
||||
AstVar* varp = it->second;
|
||||
if (modp == smodp) {
|
||||
|
|
@ -166,7 +166,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
|||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
// Collect list of scopes
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
varsExpand();
|
||||
|
||||
// Sort by names, so line/process order matters less
|
||||
|
|
@ -185,7 +185,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
|||
nameCheck(nodep);
|
||||
m_modp = nodep;
|
||||
m_labelNum = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
|
|
@ -210,7 +210,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
|||
}
|
||||
virtual void visit(AstVar* nodep) {
|
||||
nameCheck(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->isSigUserRdPublic()
|
||||
&& !nodep->isParam()) { // The VPI functions require a pointer to allow modification, but parameters are constants
|
||||
m_modVars.push_back(make_pair(m_modp, nodep));
|
||||
|
|
@ -224,7 +224,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
|||
}
|
||||
virtual void visit(AstJumpLabel* nodep) {
|
||||
nodep->labelNum(++m_labelNum);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
nameCheck(nodep);
|
||||
|
|
@ -232,14 +232,14 @@ class EmitCSyms : EmitCBaseVisitor {
|
|||
m_dpis.push_back(nodep);
|
||||
}
|
||||
m_funcp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_funcp = NULL;
|
||||
}
|
||||
// NOPs
|
||||
virtual void visit(AstConst*) {}
|
||||
// Default
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//---------------------------------------
|
||||
// ACCESSORS
|
||||
|
|
@ -249,7 +249,7 @@ public:
|
|||
m_modp = NULL;
|
||||
m_coverBins = 0;
|
||||
m_labelNum = 0;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -279,13 +279,13 @@ void EmitCSyms::emitSymHdr() {
|
|||
|
||||
// for
|
||||
puts("\n// INCLUDE MODULE CLASSES\n");
|
||||
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {
|
||||
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) {
|
||||
puts("#include \""+modClassName(nodep)+".h\"\n");
|
||||
}
|
||||
|
||||
if (v3Global.dpi()) {
|
||||
puts ("\n// DPI TYPES for DPI Export callbacks (Internal use)\n");
|
||||
map<string,int> types; // Remove duplicates and sort
|
||||
std::map<string,int> types; // Remove duplicates and sort
|
||||
for (ScopeFuncs::iterator it = m_scopeFuncs.begin(); it != m_scopeFuncs.end(); ++it) {
|
||||
AstCFunc* funcp = it->second.m_funcp;
|
||||
if (funcp->dpiExport()) {
|
||||
|
|
@ -293,7 +293,7 @@ void EmitCSyms::emitSymHdr() {
|
|||
types["typedef void (*"+cbtype+") ("+cFuncArgs(funcp)+");\n"] = 1;
|
||||
}
|
||||
}
|
||||
for (map<string,int>::iterator it = types.begin(); it != types.end(); ++it) {
|
||||
for (std::map<string,int>::iterator it = types.begin(); it != types.end(); ++it) {
|
||||
puts(it->first);
|
||||
}
|
||||
}
|
||||
|
|
@ -310,7 +310,7 @@ void EmitCSyms::emitSymHdr() {
|
|||
puts("bool __Vm_didInit;\n");
|
||||
|
||||
puts("\n// SUBCELL STATE\n");
|
||||
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
AstScope* scopep = it->first; AstNodeModule* modp = it->second;
|
||||
if (modp->isTop()) {
|
||||
ofp()->printf("%-30s ", (modClassName(modp)+"*").c_str());
|
||||
|
|
@ -370,7 +370,7 @@ void EmitCSyms::emitSymImp() {
|
|||
|
||||
// Includes
|
||||
puts("#include \""+symClassName()+".h\"\n");
|
||||
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {
|
||||
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) {
|
||||
puts("#include \""+modClassName(nodep)+".h\"\n");
|
||||
}
|
||||
|
||||
|
|
@ -386,7 +386,7 @@ void EmitCSyms::emitSymImp() {
|
|||
puts("\t, __Vm_didInit(false)\n");
|
||||
puts("\t// Setup submodule names\n");
|
||||
char comma=',';
|
||||
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
AstScope* scopep = it->first; AstNodeModule* modp = it->second;
|
||||
if (modp->isTop()) {
|
||||
} else {
|
||||
|
|
@ -404,7 +404,7 @@ void EmitCSyms::emitSymImp() {
|
|||
puts("// Pointer to top level\n");
|
||||
puts("TOPp = topp;\n");
|
||||
puts("// Setup each module's pointers to their submodules\n");
|
||||
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
AstScope* scopep = it->first; AstNodeModule* modp = it->second;
|
||||
if (!modp->isTop()) {
|
||||
string arrow = scopep->name();
|
||||
|
|
@ -421,7 +421,7 @@ void EmitCSyms::emitSymImp() {
|
|||
|
||||
puts("// Setup each module's pointer back to symbol table (for public functions)\n");
|
||||
puts("TOPp->__Vconfigure(this, true);\n");
|
||||
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
AstScope* scopep = it->first; AstNodeModule* modp = it->second;
|
||||
if (!modp->isTop()) {
|
||||
// first is used by AstCoverDecl's call to __vlCoverInsert
|
||||
|
|
@ -482,10 +482,10 @@ void EmitCSyms::emitSymImp() {
|
|||
}
|
||||
for (AstNodeDType* dtypep=varp->dtypep(); dtypep; ) {
|
||||
dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node
|
||||
if (AstNodeArrayDType* adtypep = dtypep->castNodeArrayDType()) {
|
||||
if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||
bounds += " ,"; bounds += cvtToStr(adtypep->msb());
|
||||
bounds += ","; bounds += cvtToStr(adtypep->lsb());
|
||||
if (dtypep->castPackArrayDType()) pdim++; else udim++;
|
||||
if (VN_IS(dtypep, PackArrayDType)) pdim++; else udim++;
|
||||
dtypep = adtypep->subDTypep();
|
||||
}
|
||||
else break; // AstBasicDType - nothing below, 1
|
||||
|
|
@ -534,7 +534,7 @@ void EmitCSyms::emitSymImp() {
|
|||
}
|
||||
puts( "os"+op+"__Vm_didInit;\n");
|
||||
puts( "// SUBCELL STATE\n");
|
||||
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
|
||||
AstScope* scopep = it->first; AstNodeModule* modp = it->second;
|
||||
if (!modp->isTop()) {
|
||||
puts( scopep->nameDotless()+"."+funcname+"(os);\n");
|
||||
|
|
@ -571,7 +571,7 @@ void EmitCSyms::emitDpiHdr() {
|
|||
|
||||
int firstExp = 0;
|
||||
int firstImp = 0;
|
||||
for (vector<AstCFunc*>::iterator it = m_dpis.begin(); it != m_dpis.end(); ++it) {
|
||||
for (std::vector<AstCFunc*>::iterator it = m_dpis.begin(); it != m_dpis.end(); ++it) {
|
||||
AstCFunc* nodep = *it;
|
||||
if (nodep->dpiExportWrapper()) {
|
||||
if (!firstExp++) puts("\n// DPI EXPORTS\n");
|
||||
|
|
@ -619,7 +619,7 @@ void EmitCSyms::emitDpiImp() {
|
|||
puts("#include \""+topClassName()+".h\"\n");
|
||||
puts("\n");
|
||||
|
||||
for (vector<AstCFunc*>::iterator it = m_dpis.begin(); it != m_dpis.end(); ++it) {
|
||||
for (std::vector<AstCFunc*>::iterator it = m_dpis.begin(); it != m_dpis.end(); ++it) {
|
||||
AstCFunc* nodep = *it;
|
||||
if (nodep->dpiExportWrapper()) {
|
||||
puts("#ifndef _VL_DPIDECL_"+nodep->name()+"\n");
|
||||
|
|
@ -629,7 +629,7 @@ void EmitCSyms::emitDpiImp() {
|
|||
puts("return "+topClassName()+"::"+nodep->name()+"(");
|
||||
string args;
|
||||
for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) {
|
||||
if (AstVar* portp = stmtp->castVar()) {
|
||||
if (const AstVar* portp = VN_CAST(stmtp, Var)) {
|
||||
if (portp->isIO() && !portp->isFuncReturn()) {
|
||||
if (args != "") args+= ", ";
|
||||
args += portp->name();
|
||||
|
|
|
|||
|
|
@ -40,11 +40,7 @@ class EmitMkVisitor : public EmitCBaseVisitor {
|
|||
public:
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void putMakeClassEntry(V3OutMkFile& of, const string& name) {
|
||||
of.puts("\t"+V3Os::filenameNonDirExt(name)+" \\\n");
|
||||
|
|
@ -98,11 +94,14 @@ public:
|
|||
putMakeClassEntry(of, "verilated_vcd_sc.cpp");
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.mtasks()) {
|
||||
putMakeClassEntry(of, "verilated_threads.cpp");
|
||||
}
|
||||
}
|
||||
else if (support==2 && slow) {
|
||||
}
|
||||
else {
|
||||
for (AstCFile* nodep = v3Global.rootp()->filesp(); nodep; nodep=nodep->nextp()->castCFile()) {
|
||||
for (AstCFile* nodep = v3Global.rootp()->filesp(); nodep; nodep=VN_CAST(nodep->nextp(), CFile)) {
|
||||
if (nodep->source() && nodep->slow()==(slow!=0) && nodep->support()==(support!=0)) {
|
||||
putMakeClassEntry(of, nodep->name());
|
||||
}
|
||||
|
|
|
|||
238
src/V3EmitV.cpp
238
src/V3EmitV.cpp
|
|
@ -41,11 +41,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
AstSenTree* m_sensesp;
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
virtual void puts(const string& str) = 0;
|
||||
virtual void putbs(const string& str) = 0;
|
||||
|
|
@ -63,11 +59,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
putfs(nodep, nodep->verilogKwd()+" "+modClassName(nodep)+";\n");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
putqs(nodep, "end"+nodep->verilogKwd()+"\n");
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep) {
|
||||
|
|
@ -76,7 +72,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
puts(nodep->prettyName());
|
||||
puts(";\n");
|
||||
putqs(nodep, "begin\n"); // Only putfs the first time for each visitor; later for same node is putqs
|
||||
nodep->stmtsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
putqs(nodep, "end\n");
|
||||
}
|
||||
|
||||
|
|
@ -86,64 +82,64 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
} else {
|
||||
putbs("begin : "+nodep->name()+"\n");
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
puts("end\n");
|
||||
}
|
||||
virtual void visit(AstGenerate* nodep) {
|
||||
putfs(nodep, "generate\n");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
putqs(nodep, "end\n");
|
||||
}
|
||||
virtual void visit(AstFinal* nodep) {
|
||||
putfs(nodep, "final begin\n");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
putqs(nodep, "end\n");
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) {
|
||||
putfs(nodep,"initial begin\n");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
putqs(nodep, "end\n");
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) {
|
||||
putfs(nodep,"always ");
|
||||
if (m_sensesp) m_sensesp->iterateAndNext(*this); // In active
|
||||
else nodep->sensesp()->iterateAndNext(*this);
|
||||
if (m_sensesp) iterateAndNextNull(m_sensesp); // In active
|
||||
else iterateAndNextNull(nodep->sensesp());
|
||||
putbs(" begin\n");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
putqs(nodep,"end\n");
|
||||
}
|
||||
virtual void visit(AstAlwaysPublic* nodep) {
|
||||
putfs(nodep,"/*verilator public_flat_rw ");
|
||||
if (m_sensesp) m_sensesp->iterateAndNext(*this); // In active
|
||||
else nodep->sensesp()->iterateAndNext(*this);
|
||||
if (m_sensesp) iterateAndNextNull(m_sensesp); // In active
|
||||
else iterateAndNextNull(nodep->sensesp());
|
||||
putqs(nodep," ");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
putqs(nodep,"*/\n");
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
putfs(nodep," "+nodep->verilogKwd()+" ");
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
if (!m_suppressSemi) puts(";\n");
|
||||
}
|
||||
virtual void visit(AstAssignDly* nodep) {
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
putfs(nodep," <= ");
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
puts(";\n");
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) {
|
||||
putbs("alias ");
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
putfs(nodep," = ");
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
if (!m_suppressSemi) puts(";\n");
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) {
|
||||
putfs(nodep,"assign ");
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
putbs(" = ");
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
if (!m_suppressSemi) puts(";\n");
|
||||
}
|
||||
virtual void visit(AstBreak* nodep) {
|
||||
|
|
@ -154,7 +150,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
// AstSenItem is called for dumping in isolation by V3Order
|
||||
putfs(nodep,"@(");
|
||||
for (AstNode* expp=nodep->sensesp(); expp; expp = expp->nextp()) {
|
||||
expp->accept(*this);
|
||||
iterate(expp);
|
||||
if (expp->nextp()) putqs(expp->nextp()," or ");
|
||||
}
|
||||
puts(")");
|
||||
|
|
@ -166,40 +162,40 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
putfs(nodep,"");
|
||||
puts(nodep->edgeType().verilogKwd());
|
||||
if (nodep->sensp()) puts(" ");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeCase* nodep) {
|
||||
putfs(nodep,"");
|
||||
if (AstCase* casep = nodep->castCase()) {
|
||||
if (const AstCase* casep = VN_CAST(nodep, Case)) {
|
||||
if (casep->priorityPragma()) puts("priority ");
|
||||
if (casep->uniquePragma()) puts("unique ");
|
||||
if (casep->unique0Pragma()) puts("unique0 ");
|
||||
}
|
||||
puts(nodep->verilogKwd());
|
||||
puts(" (");
|
||||
nodep->exprp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->exprp());
|
||||
puts(")\n");
|
||||
if (AstCase* casep = nodep->castCase()) {
|
||||
if (const AstCase* casep = VN_CAST(nodep, Case)) {
|
||||
if (casep->fullPragma() || casep->parallelPragma()) {
|
||||
puts(" // synopsys");
|
||||
if (casep->fullPragma()) puts(" full_case");
|
||||
if (casep->parallelPragma()) puts(" parallel_case");
|
||||
}
|
||||
}
|
||||
nodep->itemsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->itemsp());
|
||||
putqs(nodep,"endcase\n");
|
||||
}
|
||||
virtual void visit(AstCaseItem* nodep) {
|
||||
if (nodep->condsp()) {
|
||||
nodep->condsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->condsp());
|
||||
} else putbs("default");
|
||||
putfs(nodep,": begin ");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
putqs(nodep,"end\n");
|
||||
}
|
||||
virtual void visit(AstComment* nodep) {
|
||||
puts((string)"// "+nodep->name()+"\n");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstContinue* nodep) {
|
||||
putbs("continue");
|
||||
|
|
@ -212,11 +208,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
void visitNodeDisplay(AstNode* nodep, AstNode* fileOrStrgp, const string& text, AstNode* exprsp) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
if (fileOrStrgp) { fileOrStrgp->iterateAndNext(*this); putbs(","); }
|
||||
if (fileOrStrgp) { iterateAndNextNull(fileOrStrgp); putbs(","); }
|
||||
putsQuoted(text);
|
||||
for (AstNode* expp=exprsp; expp; expp = expp->nextp()) {
|
||||
puts(",");
|
||||
expp->iterateAndNext(*this);
|
||||
iterateAndNextNull(expp);
|
||||
}
|
||||
puts(");\n");
|
||||
}
|
||||
|
|
@ -241,23 +237,23 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
virtual void visit(AstFOpen* nodep) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
if (nodep->filep()) nodep->filep()->iterateAndNext(*this);
|
||||
if (nodep->filep()) iterateAndNextNull(nodep->filep());
|
||||
putbs(",");
|
||||
if (nodep->filenamep()) nodep->filenamep()->iterateAndNext(*this);
|
||||
if (nodep->filenamep()) iterateAndNextNull(nodep->filenamep());
|
||||
putbs(",");
|
||||
if (nodep->modep()) nodep->modep()->iterateAndNext(*this);
|
||||
if (nodep->modep()) iterateAndNextNull(nodep->modep());
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstFClose* nodep) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
if (nodep->filep()) nodep->filep()->iterateAndNext(*this);
|
||||
if (nodep->filep()) iterateAndNextNull(nodep->filep());
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstFFlush* nodep) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
if (nodep->filep()) nodep->filep()->iterateAndNext(*this);
|
||||
if (nodep->filep()) iterateAndNextNull(nodep->filep());
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstJumpGo* nodep) {
|
||||
|
|
@ -265,80 +261,80 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
}
|
||||
virtual void visit(AstJumpLabel* nodep) {
|
||||
putbs("begin : "+cvtToStr((void*)(nodep))+"\n");
|
||||
if (nodep->stmtsp()) nodep->stmtsp()->iterateAndNext(*this);
|
||||
if (nodep->stmtsp()) iterateAndNextNull(nodep->stmtsp());
|
||||
puts("end\n");
|
||||
}
|
||||
virtual void visit(AstNodeReadWriteMem* nodep) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
if (nodep->filenamep()) nodep->filenamep()->iterateAndNext(*this);
|
||||
if (nodep->filenamep()) iterateAndNextNull(nodep->filenamep());
|
||||
putbs(",");
|
||||
if (nodep->memp()) nodep->memp()->iterateAndNext(*this);
|
||||
if (nodep->lsbp()) { putbs(","); nodep->lsbp()->iterateAndNext(*this); }
|
||||
if (nodep->msbp()) { putbs(","); nodep->msbp()->iterateAndNext(*this); }
|
||||
if (nodep->memp()) iterateAndNextNull(nodep->memp());
|
||||
if (nodep->lsbp()) { putbs(","); iterateAndNextNull(nodep->lsbp()); }
|
||||
if (nodep->msbp()) { putbs(","); iterateAndNextNull(nodep->msbp()); }
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstSysFuncAsTask* nodep) {
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
puts(";\n");
|
||||
}
|
||||
virtual void visit(AstSysIgnore* nodep) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
nodep->exprsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->exprsp());
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstNodeFor* nodep) {
|
||||
putfs(nodep,"for (");
|
||||
m_suppressSemi = true;
|
||||
nodep->initsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->initsp());
|
||||
puts(";");
|
||||
nodep->condp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->condp());
|
||||
puts(";");
|
||||
nodep->incsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->incsp());
|
||||
m_suppressSemi = false;
|
||||
puts(") begin\n");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
putqs(nodep,"end\n");
|
||||
}
|
||||
virtual void visit(AstRepeat* nodep) {
|
||||
putfs(nodep,"repeat (");
|
||||
nodep->countp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->countp());
|
||||
puts(") begin\n");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
putfs(nodep,"end\n");
|
||||
}
|
||||
virtual void visit(AstWhile* nodep) {
|
||||
nodep->precondsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->precondsp());
|
||||
putfs(nodep,"while (");
|
||||
nodep->condp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->condp());
|
||||
puts(") begin\n");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
nodep->incsp()->iterateAndNext(*this);
|
||||
nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
iterateAndNextNull(nodep->incsp());
|
||||
iterateAndNextNull(nodep->precondsp()); // Need to recompute before next loop
|
||||
putfs(nodep,"end\n");
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
putfs(nodep,"");
|
||||
if (AstIf* ifp = nodep->castIf()) {
|
||||
if (const AstIf* ifp = VN_CAST(nodep, If)) {
|
||||
if (ifp->priorityPragma()) puts("priority ");
|
||||
if (ifp->uniquePragma()) puts("unique ");
|
||||
if (ifp->unique0Pragma()) puts("unique0 ");
|
||||
}
|
||||
puts("if (");
|
||||
nodep->condp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->condp());
|
||||
puts(") begin\n");
|
||||
nodep->ifsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
if (nodep->elsesp()) {
|
||||
putqs(nodep,"end\n");
|
||||
putqs(nodep,"else begin\n");
|
||||
nodep->elsesp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
}
|
||||
putqs(nodep,"end\n");
|
||||
}
|
||||
virtual void visit(AstReturn* nodep) {
|
||||
putfs(nodep,"return ");
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
puts(";\n");
|
||||
}
|
||||
virtual void visit(AstStop* nodep) {
|
||||
|
|
@ -354,22 +350,22 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
}
|
||||
virtual void visit(AstCStmt* nodep) {
|
||||
putfs(nodep,"$_CSTMT(");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstCMath* nodep) {
|
||||
putfs(nodep,"$_CMATH(");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstUCStmt* nodep) {
|
||||
putfs(nodep,"$c(");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstUCFunc* nodep) {
|
||||
putfs(nodep,"$c(");
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
puts(")");
|
||||
}
|
||||
|
||||
|
|
@ -399,22 +395,22 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
case 'k': putbs(""); break;
|
||||
case 'l': {
|
||||
if (!lhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
|
||||
else lhsp->iterateAndNext(*this);
|
||||
else iterateAndNextNull(lhsp);
|
||||
break;
|
||||
}
|
||||
case 'r': {
|
||||
if (!rhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
|
||||
else rhsp->iterateAndNext(*this);
|
||||
else iterateAndNextNull(rhsp);
|
||||
break;
|
||||
}
|
||||
case 't': {
|
||||
if (!thsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
|
||||
else thsp->iterateAndNext(*this);
|
||||
else iterateAndNextNull(thsp);
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
if (!nodep->dtypep()) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
|
||||
else nodep->dtypep()->iterateAndNext(*this);
|
||||
else iterateAndNextNull(nodep->dtypep());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
@ -439,10 +435,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
}
|
||||
virtual void visit(AstAttrOf* nodep) {
|
||||
putfs(nodep,"$_ATTROF(");
|
||||
nodep->fromp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->fromp());
|
||||
if (nodep->dimp()) {
|
||||
putbs(",");
|
||||
nodep->dimp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->dimp());
|
||||
}
|
||||
puts(")");
|
||||
}
|
||||
|
|
@ -453,83 +449,83 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
int index = nodep->posIndex(pos);
|
||||
puts(cvtToStr(index));
|
||||
puts(":");
|
||||
itemp->accept(*this);
|
||||
iterate(itemp);
|
||||
if (itemp->nextp()) putbs(",");
|
||||
}
|
||||
puts("}");
|
||||
}
|
||||
virtual void visit(AstNodeCond* nodep) {
|
||||
putbs("(");
|
||||
nodep->condp()->iterateAndNext(*this); putfs(nodep," ? ");
|
||||
nodep->expr1p()->iterateAndNext(*this); putbs(" : ");
|
||||
nodep->expr2p()->iterateAndNext(*this); puts(")");
|
||||
iterateAndNextNull(nodep->condp()); putfs(nodep," ? ");
|
||||
iterateAndNextNull(nodep->expr1p()); putbs(" : ");
|
||||
iterateAndNextNull(nodep->expr2p()); puts(")");
|
||||
}
|
||||
virtual void visit(AstRange* nodep) {
|
||||
puts("[");
|
||||
if (nodep->msbp()->castConst() && nodep->lsbp()->castConst()) {
|
||||
if (VN_IS(nodep->msbp(), Const) && VN_IS(nodep->lsbp(), Const)) {
|
||||
// Looks nicer if we print [1:0] rather than [32'sh1:32sh0]
|
||||
puts(cvtToStr(nodep->leftp()->castConst()->toSInt())); puts(":");
|
||||
puts(cvtToStr(nodep->rightp()->castConst()->toSInt())); puts("]");
|
||||
puts(cvtToStr(VN_CAST(nodep->leftp(), Const)->toSInt())); puts(":");
|
||||
puts(cvtToStr(VN_CAST(nodep->rightp(), Const)->toSInt())); puts("]");
|
||||
} else {
|
||||
nodep->leftp()->iterateAndNext(*this); puts(":");
|
||||
nodep->rightp()->iterateAndNext(*this); puts("]");
|
||||
iterateAndNextNull(nodep->leftp()); puts(":");
|
||||
iterateAndNextNull(nodep->rightp()); puts("]");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstSel* nodep) {
|
||||
nodep->fromp()->iterateAndNext(*this); puts("[");
|
||||
if (nodep->lsbp()->castConst()) {
|
||||
iterateAndNextNull(nodep->fromp()); puts("[");
|
||||
if (VN_IS(nodep->lsbp(), Const)) {
|
||||
if (nodep->widthp()->isOne()) {
|
||||
if (nodep->lsbp()->castConst()) {
|
||||
puts(cvtToStr(nodep->lsbp()->castConst()->toSInt()));
|
||||
if (VN_IS(nodep->lsbp(), Const)) {
|
||||
puts(cvtToStr(VN_CAST(nodep->lsbp(), Const)->toSInt()));
|
||||
} else {
|
||||
nodep->lsbp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lsbp());
|
||||
}
|
||||
} else {
|
||||
puts(cvtToStr(nodep->lsbp()->castConst()->toSInt()
|
||||
+nodep->widthp()->castConst()->toSInt()
|
||||
-1));
|
||||
puts(cvtToStr(VN_CAST(nodep->lsbp(), Const)->toSInt()
|
||||
+ VN_CAST(nodep->widthp(), Const)->toSInt()
|
||||
- 1));
|
||||
puts(":");
|
||||
puts(cvtToStr(nodep->lsbp()->castConst()->toSInt()));
|
||||
puts(cvtToStr(VN_CAST(nodep->lsbp(), Const)->toSInt()));
|
||||
}
|
||||
} else {
|
||||
nodep->lsbp()->iterateAndNext(*this); putfs(nodep,"+:");
|
||||
nodep->widthp()->iterateAndNext(*this); puts("]");
|
||||
iterateAndNextNull(nodep->lsbp()); putfs(nodep,"+:");
|
||||
iterateAndNextNull(nodep->widthp()); puts("]");
|
||||
}
|
||||
puts("]");
|
||||
}
|
||||
virtual void visit(AstSliceSel* nodep) {
|
||||
nodep->fromp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->fromp());
|
||||
puts(cvtToStr(nodep->declRange()));
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) {
|
||||
putfs(nodep,"typedef ");
|
||||
nodep->dtypep()->iterateAndNext(*this); puts(" ");
|
||||
iterateAndNextNull(nodep->dtypep()); puts(" ");
|
||||
puts(nodep->prettyName());
|
||||
puts(";\n");
|
||||
}
|
||||
virtual void visit(AstBasicDType* nodep) {
|
||||
if (nodep->isSigned()) putfs(nodep,"signed ");
|
||||
putfs(nodep,nodep->prettyName());
|
||||
if (nodep->rangep()) { puts(" "); nodep->rangep()->iterateAndNext(*this); puts(" "); }
|
||||
if (nodep->rangep()) { puts(" "); iterateAndNextNull(nodep->rangep()); puts(" "); }
|
||||
else if (nodep->isRanged()) { puts(" ["); puts(cvtToStr(nodep->msb())); puts(":0] "); }
|
||||
}
|
||||
virtual void visit(AstConstDType* nodep) {
|
||||
putfs(nodep,"const ");
|
||||
nodep->subDTypep()->accept(*this);
|
||||
iterate(nodep->subDTypep());
|
||||
}
|
||||
virtual void visit(AstNodeArrayDType* nodep) {
|
||||
nodep->subDTypep()->accept(*this);
|
||||
nodep->rangep()->iterateAndNext(*this);
|
||||
iterate(nodep->subDTypep());
|
||||
iterateAndNextNull(nodep->rangep());
|
||||
}
|
||||
virtual void visit(AstNodeClassDType* nodep) {
|
||||
puts(nodep->verilogKwd()+" ");
|
||||
if (nodep->packed()) puts("packed ");
|
||||
puts("\n");
|
||||
nodep->membersp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->membersp());
|
||||
puts("}");
|
||||
}
|
||||
virtual void visit(AstMemberDType* nodep) {
|
||||
nodep->subDTypep()->accept(*this);
|
||||
iterate(nodep->subDTypep());
|
||||
puts(" ");
|
||||
puts(nodep->name());
|
||||
puts("}");
|
||||
|
|
@ -538,11 +534,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
if (nodep->dotted()!="") { putfs(nodep,nodep->dotted()); puts("."); puts(nodep->prettyName()); }
|
||||
else { putfs(nodep,nodep->prettyName()); }
|
||||
puts("(");
|
||||
nodep->pinsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->pinsp());
|
||||
puts(")");
|
||||
}
|
||||
virtual void visit(AstArg* nodep) {
|
||||
nodep->exprp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->exprp());
|
||||
}
|
||||
// Terminals
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
|
|
@ -564,21 +560,21 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
|
||||
// Just iterate
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVar* nodep) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
puts(" ");
|
||||
nodep->dtypep()->iterate(*this); puts(" ");
|
||||
iterate(nodep->dtypep()); puts(" ");
|
||||
puts(nodep->prettyName());
|
||||
puts(";\n");
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
m_sensesp = nodep->sensesp();
|
||||
nodep->stmtsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
m_sensesp = NULL;
|
||||
}
|
||||
virtual void visit(AstVarScope*) {}
|
||||
|
|
@ -591,7 +587,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
|||
// Default
|
||||
virtual void visit(AstNode* nodep) {
|
||||
puts((string)"\n???? // "+nodep->prettyTypeName()+"\n");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Not v3fatalSrc so we keep processing
|
||||
nodep->v3error("Internal: Unknown node type reached emitter: "<<nodep->prettyTypeName());
|
||||
}
|
||||
|
|
@ -620,7 +616,7 @@ class EmitVFileVisitor : public EmitVBaseVisitor {
|
|||
public:
|
||||
EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp) {
|
||||
m_ofp = ofp;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~EmitVFileVisitor() {}
|
||||
};
|
||||
|
|
@ -630,7 +626,7 @@ public:
|
|||
|
||||
class EmitVStreamVisitor : public EmitVBaseVisitor {
|
||||
// MEMBERS
|
||||
ostream& m_os;
|
||||
std::ostream& m_os;
|
||||
// METHODS
|
||||
virtual void putsNoTracking(const string& str) { m_os<<str; }
|
||||
virtual void puts(const string& str) { putsNoTracking(str); }
|
||||
|
|
@ -638,9 +634,9 @@ class EmitVStreamVisitor : public EmitVBaseVisitor {
|
|||
virtual void putfs(AstNode*, const string& str) { putbs(str); }
|
||||
virtual void putqs(AstNode*, const string& str) { putbs(str); }
|
||||
public:
|
||||
EmitVStreamVisitor(AstNode* nodep, ostream& os)
|
||||
EmitVStreamVisitor(AstNode* nodep, std::ostream& os)
|
||||
: m_os(os) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~EmitVStreamVisitor() {}
|
||||
};
|
||||
|
|
@ -649,7 +645,7 @@ public:
|
|||
// Emit to a stream (perhaps stringstream)
|
||||
|
||||
class EmitVPrefixedFormatter : public V3OutFormatter {
|
||||
ostream& m_os;
|
||||
std::ostream& m_os;
|
||||
string m_prefix; // What to print at beginning of each line
|
||||
int m_flWidth; // Padding of fileline
|
||||
int m_column; // Rough location; need just zero or non-zero
|
||||
|
|
@ -675,7 +671,7 @@ public:
|
|||
void prefixFl(FileLine* fl) { m_prefixFl = fl; }
|
||||
FileLine* prefixFl() const { return m_prefixFl; }
|
||||
int column() const { return m_column; }
|
||||
EmitVPrefixedFormatter(ostream& os, const string& prefix, int flWidth)
|
||||
EmitVPrefixedFormatter(std::ostream& os, const string& prefix, int flWidth)
|
||||
: V3OutFormatter("__STREAM", V3OutFormatter::LA_VERILOG)
|
||||
, m_os(os), m_prefix(prefix), m_flWidth(flWidth) {
|
||||
m_column = 0;
|
||||
|
|
@ -707,11 +703,11 @@ class EmitVPrefixedVisitor : public EmitVBaseVisitor {
|
|||
}
|
||||
|
||||
public:
|
||||
EmitVPrefixedVisitor(AstNode* nodep, ostream& os, const string& prefix, int flWidth,
|
||||
EmitVPrefixedVisitor(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth,
|
||||
AstSenTree* domainp, bool user3mark)
|
||||
: EmitVBaseVisitor(domainp), m_formatter(os, prefix, flWidth) {
|
||||
if (user3mark) { AstUser3InUse::check(); }
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~EmitVPrefixedVisitor() {}
|
||||
};
|
||||
|
|
@ -729,7 +725,7 @@ void V3EmitV::emitv() {
|
|||
EmitVFileVisitor visitor (v3Global.rootp(), &of);
|
||||
} else {
|
||||
// Process each module in turn
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=modp->nextp()->castNodeModule()) {
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) {
|
||||
V3OutVFile of (v3Global.opt.makeDir()
|
||||
+"/"+EmitCBaseVisitor::modClassName(modp)+"__Vout.v");
|
||||
of.putsHeader();
|
||||
|
|
@ -738,11 +734,11 @@ void V3EmitV::emitv() {
|
|||
}
|
||||
}
|
||||
|
||||
void V3EmitV::verilogForTree(AstNode* nodep, ostream& os) {
|
||||
void V3EmitV::verilogForTree(AstNode* nodep, std::ostream& os) {
|
||||
EmitVStreamVisitor(nodep, os);
|
||||
}
|
||||
|
||||
void V3EmitV::verilogPrefixedTree(AstNode* nodep, ostream& os, const string& prefix, int flWidth,
|
||||
void V3EmitV::verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth,
|
||||
AstSenTree* domainp, bool user3mark) {
|
||||
EmitVPrefixedVisitor(nodep, os, prefix, flWidth, domainp, user3mark);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@
|
|||
class V3EmitV {
|
||||
public:
|
||||
static void emitv();
|
||||
static void verilogForTree(AstNode* nodep, ostream& os=cout);
|
||||
static void verilogPrefixedTree(AstNode* nodep, ostream& os, const string& prefix, int flWidth,
|
||||
static void verilogForTree(AstNode* nodep, std::ostream& os=std::cout);
|
||||
static void verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth,
|
||||
AstSenTree* domainp, bool user3percent);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -46,11 +46,7 @@ class EmitXmlFileVisitor : public AstNVisitor {
|
|||
uint64_t m_id;
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// Outfile methods
|
||||
V3OutFile* ofp() const { return m_ofp; }
|
||||
|
|
@ -76,10 +72,10 @@ class EmitXmlFileVisitor : public AstNVisitor {
|
|||
void outputTag(AstNode* nodep, string tag) {
|
||||
if (tag=="") tag = VString::downcase(nodep->typeName());
|
||||
puts("<"+tag+" "+nodep->fileline()->xml());
|
||||
if (nodep->castNodeDType()) { puts(" id="); outputId(nodep); }
|
||||
if (VN_IS(nodep, NodeDType)) { puts(" id="); outputId(nodep); }
|
||||
if (nodep->name()!="") { puts(" name="); putsQuoted(nodep->prettyName()); }
|
||||
if (nodep->tag()!="") { puts(" tag="); putsQuoted(nodep->tag()); }
|
||||
if (AstNodeDType* dtp = nodep->castNodeDType()) {
|
||||
if (AstNodeDType* dtp = VN_CAST(nodep, NodeDType)) {
|
||||
if (dtp->subDTypep()) { puts(" sub_dtype_id="); outputId(dtp->subDTypep()->skipRefp()); }
|
||||
} else {
|
||||
if (nodep->dtypep()) { puts(" dtype_id="); outputId(nodep->dtypep()->skipRefp()); }
|
||||
|
|
@ -89,7 +85,7 @@ class EmitXmlFileVisitor : public AstNVisitor {
|
|||
if (tag=="") tag = VString::downcase(nodep->typeName());
|
||||
if (nodep->op1p() || nodep->op2p() || nodep->op3p() || nodep->op4p()) {
|
||||
puts(">\n");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
puts("</"+tag+">\n");
|
||||
} else {
|
||||
puts("/>\n");
|
||||
|
|
@ -108,7 +104,7 @@ class EmitXmlFileVisitor : public AstNVisitor {
|
|||
}
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
puts("<netlist>\n");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
puts("</netlist>\n");
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
|
|
@ -154,7 +150,7 @@ public:
|
|||
EmitXmlFileVisitor(AstNode* nodep, V3OutFile* ofp) {
|
||||
m_ofp = ofp;
|
||||
m_id = 0;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~EmitXmlFileVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ int V3Error::s_debugDefault = 0;
|
|||
int V3Error::s_errorLimit = V3Error::MAX_ERRORS;
|
||||
bool V3Error::s_warnFatal = true;
|
||||
int V3Error::s_tellManual = 0;
|
||||
ostringstream V3Error::s_errorStr; // Error string being formed
|
||||
std::ostringstream V3Error::s_errorStr; // Error string being formed
|
||||
V3ErrorCode V3Error::s_errorCode = V3ErrorCode::EC_FATAL;
|
||||
bool V3Error::s_errorSuppressed = false;
|
||||
bool V3Error::s_describedEachWarn[V3ErrorCode::_ENUM_MAX];
|
||||
|
|
@ -81,10 +81,10 @@ void V3Error::init() {
|
|||
}
|
||||
|
||||
string V3Error::lineStr (const char* filename, int lineno) {
|
||||
ostringstream out;
|
||||
std::ostringstream out;
|
||||
const char* fnslashp = strrchr (filename, '/');
|
||||
if (fnslashp) filename = fnslashp+1;
|
||||
out<<filename<<":"<<dec<<lineno<<":";
|
||||
out<<filename<<":"<<std::dec<<lineno<<":";
|
||||
const char* const spaces = " ";
|
||||
size_t numsp = out.str().length(); if (numsp>20) numsp = 20;
|
||||
out<<(spaces + numsp);
|
||||
|
|
@ -101,11 +101,11 @@ void V3Error::incErrors() {
|
|||
void V3Error::abortIfWarnings() {
|
||||
bool exwarn = warnFatal() && warnCount();
|
||||
if (errorCount() && exwarn) {
|
||||
v3fatal ("Exiting due to "<<dec<<errorCount()<<" error(s), "<<warnCount()<<" warning(s)\n");
|
||||
v3fatal("Exiting due to "<<std::dec<<errorCount()<<" error(s), "<<warnCount()<<" warning(s)\n");
|
||||
} else if (errorCount()) {
|
||||
v3fatal ("Exiting due to "<<dec<<errorCount()<<" error(s)\n");
|
||||
v3fatal("Exiting due to "<<std::dec<<errorCount()<<" error(s)\n");
|
||||
} else if (exwarn) {
|
||||
v3fatal ("Exiting due to "<<dec<<warnCount()<<" warning(s)\n");
|
||||
v3fatal("Exiting due to "<<std::dec<<warnCount()<<" warning(s)\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +139,7 @@ string V3Error::msgPrefix() {
|
|||
|
||||
void V3Error::vlAbort () {
|
||||
if (V3Error::debugDefault()) {
|
||||
cerr<<msgPrefix()<<"Aborting since under --debug"<<endl;
|
||||
std::cerr<<msgPrefix()<<"Aborting since under --debug"<<endl;
|
||||
abort();
|
||||
} else {
|
||||
exit(10);
|
||||
|
|
@ -162,7 +162,7 @@ string V3Error::warnMore() {
|
|||
return msgPrefix();
|
||||
}
|
||||
|
||||
void V3Error::v3errorEnd (ostringstream& sstr) {
|
||||
void V3Error::v3errorEnd(std::ostringstream& sstr) {
|
||||
#if defined(__COVERITY__) || defined(__cppcheck__)
|
||||
if (s_errorCode==V3ErrorCode::EC_FATAL) __coverity_panic__(x);
|
||||
#endif
|
||||
|
|
@ -176,20 +176,20 @@ void V3Error::v3errorEnd (ostringstream& sstr) {
|
|||
if (s_messages.find(msg) != s_messages.end()) return;
|
||||
s_messages.insert(msg);
|
||||
// Output
|
||||
cerr<<msg;
|
||||
std::cerr<<msg;
|
||||
if (!s_errorSuppressed && !(s_errorCode==V3ErrorCode::EC_INFO
|
||||
|| s_errorCode==V3ErrorCode::USERINFO)) {
|
||||
if (!s_describedEachWarn[s_errorCode]
|
||||
&& !s_pretendError[s_errorCode]) {
|
||||
s_describedEachWarn[s_errorCode] = true;
|
||||
if (s_errorCode>=V3ErrorCode::EC_FIRST_WARN && !s_describedWarnings) {
|
||||
cerr<<msgPrefix()<<"Use \"/* verilator lint_off "<<s_errorCode.ascii()
|
||||
<<" */\" and lint_on around source to disable this message."<<endl;
|
||||
std::cerr<<msgPrefix()<<"Use \"/* verilator lint_off "<<s_errorCode.ascii()
|
||||
<<" */\" and lint_on around source to disable this message."<<endl;
|
||||
s_describedWarnings = true;
|
||||
}
|
||||
if (s_errorCode.dangerous()) {
|
||||
cerr<<msgPrefix()<<"*** See the manual before disabling this,"<<endl;
|
||||
cerr<<msgPrefix()<<"else you may end up with different sim results."<<endl;
|
||||
std::cerr<<msgPrefix()<<"*** See the manual before disabling this,"<<endl;
|
||||
std::cerr<<msgPrefix()<<"else you may end up with different sim results."<<endl;
|
||||
}
|
||||
}
|
||||
// If first warning is not the user's fault (internal/unsupported) then give the website
|
||||
|
|
@ -210,7 +210,7 @@ void V3Error::v3errorEnd (ostringstream& sstr) {
|
|||
if (!inFatal) {
|
||||
inFatal = true;
|
||||
if (s_tellManual==1) {
|
||||
cerr<<msgPrefix()<<"See the manual and http://www.veripool.org/verilator for more assistance."<<endl;
|
||||
std::cerr<<msgPrefix()<<"See the manual and http://www.veripool.org/verilator for more assistance."<<endl;
|
||||
s_tellManual = 2;
|
||||
}
|
||||
#ifndef _V3ERROR_NO_GLOBAL_
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ public:
|
|||
UNDRIVEN, // No drivers
|
||||
UNOPT, // Unoptimizable block
|
||||
UNOPTFLAT, // Unoptimizable block after flattening
|
||||
UNOPTTHREADS, // Thread partitioner unable to fill all requested threads
|
||||
UNPACKED, // Unsupported unpacked
|
||||
UNSIGNED, // Comparison is constant due to unsigned arithmetic
|
||||
UNUSED, // No receivers
|
||||
|
|
@ -130,7 +131,7 @@ public:
|
|||
"ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN",
|
||||
"BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE",
|
||||
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CDCRSTLOGIC", "CLKDATA",
|
||||
"CMPCONST", "COLONPLUS", "COMBDLY", "DEFPARAM", "DECLFILENAME",
|
||||
"CMPCONST", "COLONPLUS", "COMBDLY", "DEFPARAM", "DECLFILENAME",
|
||||
"ENDLABEL", "GENCLK",
|
||||
"IFDEPTH", "IMPERFECTSCH", "IMPLICIT", "IMPURE",
|
||||
"INCABSPATH", "INFINITELOOP", "INITIALDLY",
|
||||
|
|
@ -139,7 +140,8 @@ public:
|
|||
"PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY",
|
||||
"REALCVT", "REDEFMACRO",
|
||||
"SELRANGE", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
|
||||
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNPACKED", "UNSIGNED", "UNUSED",
|
||||
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
|
||||
"UNPACKED", "UNSIGNED", "UNUSED",
|
||||
"USERERROR", "USERFATAL", "USERINFO", "USERWARN",
|
||||
"VARHIDDEN", "WIDTH", "WIDTHCONCAT",
|
||||
" MAX"
|
||||
|
|
@ -189,14 +191,14 @@ public:
|
|||
inline bool operator== (V3ErrorCode lhs, V3ErrorCode rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (V3ErrorCode lhs, V3ErrorCode::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (V3ErrorCode::en lhs, V3ErrorCode rhs) { return (lhs == rhs.m_e); }
|
||||
inline ostream& operator<<(ostream& os, V3ErrorCode rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, V3ErrorCode rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
class V3Error {
|
||||
// Base class for any object that wants debugging and error reporting
|
||||
|
||||
typedef set<string> MessagesSet;
|
||||
typedef std::set<string> MessagesSet;
|
||||
typedef void (*ErrorExitCb)(void);
|
||||
|
||||
private:
|
||||
|
|
@ -209,7 +211,7 @@ class V3Error {
|
|||
static int s_errCount; // Error count
|
||||
static int s_warnCount; // Warning count
|
||||
static int s_tellManual; // Tell user to see manual, 0=not yet, 1=doit, 2=disable
|
||||
static ostringstream s_errorStr; // Error string being formed
|
||||
static std::ostringstream s_errorStr; // Error string being formed
|
||||
static V3ErrorCode s_errorCode; // Error string being formed will abort
|
||||
static bool s_errorSuppressed; // Error being formed should be suppressed
|
||||
static MessagesSet s_messages; // What errors we've outputted
|
||||
|
|
@ -217,7 +219,7 @@ class V3Error {
|
|||
|
||||
enum MaxErrors { MAX_ERRORS = 50 }; // Fatal after this may errors
|
||||
|
||||
V3Error() { cerr<<("Static class"); abort(); }
|
||||
V3Error() { std::cerr<<("Static class"); abort(); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -252,15 +254,15 @@ class V3Error {
|
|||
// Error end takes the string stream to output, be careful to seek() as needed
|
||||
static void v3errorPrep(V3ErrorCode code) {
|
||||
s_errorStr.str(""); s_errorCode=code; s_errorSuppressed=false; }
|
||||
static ostringstream& v3errorStr() { return s_errorStr; }
|
||||
static std::ostringstream& v3errorStr() { return s_errorStr; }
|
||||
static void vlAbort();
|
||||
static void v3errorEnd(ostringstream& sstr); // static, but often overridden in classes.
|
||||
static void v3errorEnd(std::ostringstream& sstr); // static, but often overridden in classes.
|
||||
};
|
||||
|
||||
// Global versions, so that if the class doesn't define a operator, we get the functions anyways.
|
||||
inline int debug() { return V3Error::debugDefault(); }
|
||||
inline void v3errorEnd(ostringstream& sstr) { V3Error::v3errorEnd(sstr); }
|
||||
inline void v3errorEndFatal(ostringstream& sstr) { V3Error::v3errorEnd(sstr); assert(0); VL_UNREACHABLE }
|
||||
inline void v3errorEnd(std::ostringstream& sstr) { V3Error::v3errorEnd(sstr); }
|
||||
inline void v3errorEndFatal(std::ostringstream& sstr) { V3Error::v3errorEnd(sstr); assert(0); VL_UNREACHABLE }
|
||||
|
||||
// Theses allow errors using << operators: v3error("foo"<<"bar");
|
||||
// Careful, you can't put () around msg, as you would in most macro definitions
|
||||
|
|
@ -276,7 +278,11 @@ inline void v3errorEndFatal(ostringstream& sstr) { V3Error::v3errorEnd(sstr); as
|
|||
#define v3error(msg) v3warnCode(V3ErrorCode::EC_ERROR, msg)
|
||||
#define v3fatal(msg) v3warnCodeFatal(V3ErrorCode::EC_FATAL, msg)
|
||||
// Use this instead of fatal() to mention the source code line.
|
||||
#define v3fatalSrc(msg) v3warnCodeFatal(V3ErrorCode::EC_FATALSRC, __FILE__<<":"<<dec<<__LINE__<<": "<<msg)
|
||||
#define v3fatalSrc(msg) v3warnCodeFatal(V3ErrorCode::EC_FATALSRC, __FILE__<<":"<<std::dec<<__LINE__<<": "<<msg)
|
||||
// Use this when normal v3fatal is called in static method that overrides fileline.
|
||||
#define v3fatalStatic(msg) \
|
||||
::v3errorEndFatal((V3Error::v3errorPrep(V3ErrorCode::EC_FATAL), \
|
||||
(V3Error::v3errorStr()<<msg), V3Error::v3errorStr()));
|
||||
|
||||
#define UINFO(level,stmsg) {if(VL_UNLIKELY(debug()>=(level))) { cout<<"- "<<V3Error::lineStr(__FILE__,__LINE__)<<stmsg; }}
|
||||
#define UINFONL(level,stmsg) {if(VL_UNLIKELY(debug()>=(level))) { cout<<stmsg; } }
|
||||
|
|
@ -289,15 +295,31 @@ inline void v3errorEndFatal(ostringstream& sstr) { V3Error::v3errorEnd(sstr); as
|
|||
|
||||
#define UASSERT(condition,stmsg) { if (VL_UNLIKELY(!(condition))) { v3fatalSrc(stmsg); }}
|
||||
// For use in V3Ast static functions only
|
||||
#define UASSERT_STATIC(condition,stmsg) { if (VL_UNLIKELY(!(condition))) { \
|
||||
cerr<<"Internal Error: "<<__FILE__<<":"<<dec<<__LINE__<<":"<<(stmsg)<<endl; abort(); } }
|
||||
#define UASSERT_STATIC(condition,stmsg) \
|
||||
{ if (VL_UNLIKELY(!(condition))) { \
|
||||
std::cerr<<"Internal Error: "<<__FILE__<<":"<<std::dec<<__LINE__<<":"<<(stmsg)<<std::endl; abort(); } }
|
||||
// Check self test values for expected value. Safe from side-effects.
|
||||
// Type argument can be removed when go to C++11 (use auto).
|
||||
#define UASSERT_SELFTEST(Type,got,exp) \
|
||||
do { Type g = (got); Type e = (exp); \
|
||||
UASSERT(g==e, "Self-test failed '" #got "==" #exp "'"" got=" \
|
||||
<<g<<" expected="<<e); } while(0)
|
||||
|
||||
#define V3ERROR_NA { v3error("Internal: Unexpected Call"); v3fatalSrc("Unexpected Call"); }
|
||||
|
||||
/// Declare a convenience debug() routine that may be added to any class in
|
||||
/// Verilator so that --debugi-<srcfile> will work to control UINFOs in
|
||||
/// that class:
|
||||
#define VL_DEBUG_FUNC static int debug() { \
|
||||
static int level = -1; \
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); \
|
||||
return level; \
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
template< class T> std::string cvtToStr (const T& t) {
|
||||
ostringstream os; os<<t; return os.str();
|
||||
std::ostringstream os; os<<t; return os.str();
|
||||
}
|
||||
|
||||
inline uint32_t cvtToHash(const void* vp) {
|
||||
|
|
|
|||
|
|
@ -53,11 +53,7 @@ private:
|
|||
AstNode* m_stmtp; // Current statement
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
int longOrQuadWidth (AstNode* nodep) {
|
||||
// Return 32 or 64...
|
||||
|
|
@ -107,7 +103,7 @@ private:
|
|||
void fixCloneLvalue (AstNode* nodep) {
|
||||
// In AstSel transforms, we call clone() on VarRefs that were lvalues,
|
||||
// but are now being used on the RHS of the assignment
|
||||
if (nodep->castVarRef()) nodep->castVarRef()->lvalue(false);
|
||||
if (VN_IS(nodep, VarRef)) VN_CAST(nodep, VarRef)->lvalue(false);
|
||||
// Iterate
|
||||
if (nodep->op1p()) fixCloneLvalue(nodep->op1p());
|
||||
if (nodep->op2p()) fixCloneLvalue(nodep->op2p());
|
||||
|
|
@ -178,9 +174,9 @@ private:
|
|||
|
||||
AstNode* newSelBitWord(AstNode* lsbp, int wordAdder) {
|
||||
// Return equation to get the VL_BITWORD of a constant or non-constant
|
||||
if (lsbp->castConst()) {
|
||||
if (VN_IS(lsbp, Const)) {
|
||||
return new AstConst (lsbp->fileline(),
|
||||
wordAdder + VL_BITWORD_I(lsbp->castConst()->toUInt()));
|
||||
wordAdder + VL_BITWORD_I(VN_CAST(lsbp, Const)->toUInt()));
|
||||
} else {
|
||||
AstNode* shiftp = new AstShiftR (lsbp->fileline(),
|
||||
lsbp->cloneTree(true),
|
||||
|
|
@ -201,17 +197,17 @@ private:
|
|||
// If there's a CONDBOUND safety to keep arrays in bounds,
|
||||
// we're going to AND it to a value that always fits inside a
|
||||
// word, so we don't need it.
|
||||
//if (nodep->castCondBound() && nodep->castCondBound()->lhsp()->castLte()) {
|
||||
// nodep = nodep->castCondBound()->rhsp();
|
||||
//if (VN_IS(nodep, CondBound) && VN_IS(VN_CAST(nodep, CondBound)->lhsp(), Lte)) {
|
||||
// nodep = VN_CAST(nodep, CondBound)->rhsp();
|
||||
//}
|
||||
return nodep;
|
||||
}
|
||||
|
||||
AstNode* newSelBitBit(AstNode* lsbp) {
|
||||
// Return equation to get the VL_BITBIT of a constant or non-constant
|
||||
if (lsbp->castConst()) {
|
||||
if (VN_IS(lsbp, Const)) {
|
||||
return new AstConst (lsbp->fileline(),
|
||||
VL_BITBIT_I(lsbp->castConst()->toUInt()));
|
||||
VL_BITBIT_I(VN_CAST(lsbp, Const)->toUInt()));
|
||||
} else {
|
||||
return new AstAnd (lsbp->fileline(),
|
||||
new AstConst(lsbp->fileline(), VL_WORDSIZE-1),
|
||||
|
|
@ -243,7 +239,7 @@ private:
|
|||
}
|
||||
bool expandWide (AstNodeAssign* nodep, AstArraySel* rhsp) {
|
||||
UINFO(8," Wordize ASSIGN(ARRAYSEL) "<<nodep<<endl);
|
||||
if (nodep->dtypep()->skipRefp()->castUnpackArrayDType()) {
|
||||
if (VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
nodep->v3fatalSrc("ArraySel with unpacked arrays should have been removed in V3Slice");
|
||||
}
|
||||
for (int w=0; w<nodep->widthWords(); w++) {
|
||||
|
|
@ -312,7 +308,7 @@ private:
|
|||
// VISITORS
|
||||
virtual void visit(AstExtend* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->isWide()) {
|
||||
// See under ASSIGN(EXTEND)
|
||||
} else {
|
||||
|
|
@ -351,10 +347,10 @@ private:
|
|||
|
||||
virtual void visit(AstSel* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Remember, Sel's may have non-integer rhs, so need to optimize for that!
|
||||
if (nodep->widthMin()!=(int)nodep->widthConst()) nodep->v3fatalSrc("Width mismatch");
|
||||
if (nodep->backp()->castNodeAssign() && nodep==nodep->backp()->castNodeAssign()->lhsp()) {
|
||||
if (VN_IS(nodep->backp(), NodeAssign) && nodep==VN_CAST(nodep->backp(), NodeAssign)->lhsp()) {
|
||||
// Sel is an LHS assignment select
|
||||
} else if (nodep->isWide()) {
|
||||
// See under ASSIGN(WIDE)
|
||||
|
|
@ -462,7 +458,7 @@ private:
|
|||
|
||||
bool expandWide (AstNodeAssign* nodep, AstSel* rhsp) {
|
||||
if (nodep->widthMin()!=(int)rhsp->widthConst()) nodep->v3fatalSrc("Width mismatch");
|
||||
if (rhsp->lsbp()->castConst() && VL_BITBIT_I(rhsp->lsbConst())==0) {
|
||||
if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_I(rhsp->lsbConst())==0) {
|
||||
int lsb = rhsp->lsbConst();
|
||||
UINFO(8," Wordize ASSIGN(SEL,align) "<<nodep<<endl);
|
||||
for (int w=0; w<nodep->widthWords(); w++) {
|
||||
|
|
@ -515,7 +511,7 @@ private:
|
|||
// Yuk.
|
||||
bool destwide = lhsp->fromp()->isWide();
|
||||
bool ones = nodep->rhsp()->isAllOnesV();
|
||||
if (lhsp->lsbp()->castConst()) {
|
||||
if (VN_IS(lhsp->lsbp(), Const)) {
|
||||
// The code should work without this constant test, but it won't
|
||||
// constify as nicely as we'd like.
|
||||
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
|
|
@ -652,7 +648,7 @@ private:
|
|||
|
||||
virtual void visit(AstConcat* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->isWide()) {
|
||||
// See under ASSIGN(WIDE)
|
||||
} else {
|
||||
|
|
@ -692,7 +688,7 @@ private:
|
|||
|
||||
virtual void visit(AstReplicate* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->isWide()) {
|
||||
// See under ASSIGN(WIDE)
|
||||
} else {
|
||||
|
|
@ -704,7 +700,7 @@ private:
|
|||
newp = new AstNegate (nodep->fileline(), lhsp);
|
||||
} else {
|
||||
UINFO(8," REPLICATE "<<nodep<<endl);
|
||||
AstConst* constp = nodep->rhsp()->castConst();
|
||||
const AstConst* constp = VN_CAST(nodep->rhsp(), Const);
|
||||
if (!constp) nodep->v3fatalSrc("Replication value isn't a constant. Checked earlier!");
|
||||
uint32_t times = constp->toUInt();
|
||||
if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCCast(nodep->fileline(), lhsp, nodep);
|
||||
|
|
@ -729,7 +725,7 @@ private:
|
|||
UINFO(8," Wordize ASSIGN(REPLICATE) "<<nodep<<endl);
|
||||
AstNode* lhsp = rhsp->lhsp();
|
||||
int lhswidth = lhsp->widthMin();
|
||||
AstConst* constp = rhsp->rhsp()->castConst();
|
||||
const AstConst* constp = VN_CAST(rhsp->rhsp(), Const);
|
||||
if (!constp) rhsp->v3fatalSrc("Replication value isn't a constant. Checked earlier!");
|
||||
uint32_t times = constp->toUInt();
|
||||
for (int w=0; w<rhsp->widthWords(); w++) {
|
||||
|
|
@ -753,7 +749,7 @@ private:
|
|||
|
||||
virtual void visit(AstChangeXor* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
UINFO(8," Wordize ChangeXor "<<nodep<<endl);
|
||||
// -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}}
|
||||
AstNode* newp = NULL;
|
||||
|
|
@ -768,7 +764,7 @@ private:
|
|||
|
||||
void visitEqNeq(AstNodeBiop* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->lhsp()->isWide()) {
|
||||
UINFO(8," Wordize EQ/NEQ "<<nodep<<endl);
|
||||
// -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}}
|
||||
|
|
@ -779,7 +775,7 @@ private:
|
|||
newAstWordSelClone (nodep->rhsp(), w));
|
||||
newp = (newp==NULL) ? eqp : (new AstOr (nodep->fileline(), newp, eqp));
|
||||
}
|
||||
if (nodep->castNeq()) {
|
||||
if (VN_IS(nodep, Neq)) {
|
||||
newp = new AstNeq (nodep->fileline(),
|
||||
new AstConst (nodep->fileline(), 0), newp);
|
||||
} else {
|
||||
|
|
@ -794,7 +790,7 @@ private:
|
|||
|
||||
virtual void visit(AstRedOr* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->lhsp()->isWide()) {
|
||||
UINFO(8," Wordize REDOR "<<nodep<<endl);
|
||||
// -> (0!={or{for each_word{WORDSEL(lhs,#)}}}
|
||||
|
|
@ -818,7 +814,7 @@ private:
|
|||
}
|
||||
virtual void visit(AstRedAnd* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->lhsp()->isWide()) {
|
||||
UINFO(8," Wordize REDAND "<<nodep<<endl);
|
||||
// -> (0!={and{for each_word{WORDSEL(lhs,#)}}}
|
||||
|
|
@ -849,7 +845,7 @@ private:
|
|||
}
|
||||
virtual void visit(AstRedXor* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->lhsp()->isWide()) {
|
||||
UINFO(8," Wordize REDXOR "<<nodep<<endl);
|
||||
// -> (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}}
|
||||
|
|
@ -869,44 +865,44 @@ private:
|
|||
virtual void visit(AstNodeStmt* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
m_stmtp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_stmtp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
m_stmtp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
bool did = false;
|
||||
if (nodep->isWide() && ((nodep->lhsp()->castVarRef()
|
||||
|| nodep->lhsp()->castArraySel()))
|
||||
if (nodep->isWide() && ((VN_IS(nodep->lhsp(), VarRef)
|
||||
|| VN_IS(nodep->lhsp(), ArraySel)))
|
||||
&& !AstVar::scVarRecurse(nodep->lhsp()) // Need special function for SC
|
||||
&& !AstVar::scVarRecurse(nodep->rhsp())) {
|
||||
if (AstConst* rhsp = nodep->rhsp()->castConst()) {
|
||||
if (AstConst* rhsp = VN_CAST(nodep->rhsp(), Const)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
} else if (AstVarRef* rhsp = nodep->rhsp()->castVarRef()) {
|
||||
} else if (AstVarRef* rhsp = VN_CAST(nodep->rhsp(), VarRef)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
} else if (AstSel* rhsp = nodep->rhsp()->castSel()) {
|
||||
} else if (AstSel* rhsp = VN_CAST(nodep->rhsp(), Sel)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
} else if (AstArraySel* rhsp = nodep->rhsp()->castArraySel()) {
|
||||
} else if (AstArraySel* rhsp = VN_CAST(nodep->rhsp(), ArraySel)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
} else if (AstConcat* rhsp = nodep->rhsp()->castConcat()) {
|
||||
} else if (AstConcat* rhsp = VN_CAST(nodep->rhsp(), Concat)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
} else if (AstReplicate* rhsp = nodep->rhsp()->castReplicate()) {
|
||||
} else if (AstReplicate* rhsp = VN_CAST(nodep->rhsp(), Replicate)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
} else if (AstAnd* rhsp = nodep->rhsp()->castAnd()) {
|
||||
} else if (AstAnd* rhsp = VN_CAST(nodep->rhsp(), And)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
} else if (AstOr* rhsp = nodep->rhsp()->castOr()) {
|
||||
} else if (AstOr* rhsp = VN_CAST(nodep->rhsp(), Or)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
} else if (AstNot* rhsp = nodep->rhsp()->castNot()) {
|
||||
} else if (AstNot* rhsp = VN_CAST(nodep->rhsp(), Not)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
} else if (AstXor* rhsp = nodep->rhsp()->castXor()) {
|
||||
} else if (AstXor* rhsp = VN_CAST(nodep->rhsp(), Xor)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
} else if (AstXnor* rhsp = nodep->rhsp()->castXnor()) {
|
||||
} else if (AstXnor* rhsp = VN_CAST(nodep->rhsp(), Xnor)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
} else if (AstNodeCond* rhsp = nodep->rhsp()->castNodeCond()) {
|
||||
} else if (AstNodeCond* rhsp = VN_CAST(nodep->rhsp(), NodeCond)) {
|
||||
did = expandWide(nodep,rhsp);
|
||||
}
|
||||
} else if (AstSel* lhsp = nodep->lhsp()->castSel()) {
|
||||
} else if (AstSel* lhsp = VN_CAST(nodep->lhsp(), Sel)) {
|
||||
did = expandLhs(nodep,lhsp);
|
||||
}
|
||||
// Cleanup common code
|
||||
|
|
@ -920,14 +916,14 @@ private:
|
|||
// Default: Just iterate
|
||||
virtual void visit(AstVar*) {} // Don't hit varrefs under vars
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit ExpandVisitor(AstNetlist* nodep) {
|
||||
m_stmtp=NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ExpandVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -97,8 +97,8 @@ class V3FileDependImp {
|
|||
};
|
||||
|
||||
// MEMBERS
|
||||
set<string> m_filenameSet; // Files generated (elim duplicates)
|
||||
set<DependFile> m_filenameList; // Files sourced/generated
|
||||
std::set<string> m_filenameSet; // Files generated (elim duplicates)
|
||||
std::set<DependFile> m_filenameList; // Files sourced/generated
|
||||
|
||||
static string stripQuotes(const string& in) {
|
||||
string pretty = in;
|
||||
|
|
@ -137,7 +137,7 @@ inline void V3FileDependImp::writeDepend(const string& filename) {
|
|||
const vl_unique_ptr<std::ofstream> ofp (V3File::new_ofstream(filename));
|
||||
if (ofp->fail()) v3fatal("Can't write "<<filename);
|
||||
|
||||
for (set<DependFile>::iterator iter=m_filenameList.begin();
|
||||
for (std::set<DependFile>::iterator iter=m_filenameList.begin();
|
||||
iter!=m_filenameList.end(); ++iter) {
|
||||
if (iter->target()) {
|
||||
*ofp<<iter->filename()<<" ";
|
||||
|
|
@ -149,7 +149,7 @@ inline void V3FileDependImp::writeDepend(const string& filename) {
|
|||
*ofp<<V3PreShell::dependFiles();
|
||||
*ofp<<" ";
|
||||
|
||||
for (set<DependFile>::iterator iter=m_filenameList.begin();
|
||||
for (std::set<DependFile>::iterator iter=m_filenameList.begin();
|
||||
iter!=m_filenameList.end(); ++iter) {
|
||||
if (!iter->target()) {
|
||||
*ofp<<iter->filename()<<" ";
|
||||
|
|
@ -160,7 +160,7 @@ inline void V3FileDependImp::writeDepend(const string& filename) {
|
|||
|
||||
if (v3Global.opt.makePhony()) {
|
||||
*ofp<<endl;
|
||||
for (set<DependFile>::iterator iter=m_filenameList.begin();
|
||||
for (std::set<DependFile>::iterator iter=m_filenameList.begin();
|
||||
iter!=m_filenameList.end(); ++iter) {
|
||||
if (!iter->target()) {
|
||||
*ofp<<iter->filename()<<":"<<endl;
|
||||
|
|
@ -177,7 +177,7 @@ inline void V3FileDependImp::writeTimes(const string& filename, const string& cm
|
|||
*ofp<<"# DESCR"<<"IPTION: Verilator output: Timestamp data for --skip-identical. Delete at will."<<endl;
|
||||
*ofp<<"C \""<<cmdline<<"\""<<endl;
|
||||
|
||||
for (set<DependFile>::iterator iter=m_filenameList.begin();
|
||||
for (std::set<DependFile>::iterator iter=m_filenameList.begin();
|
||||
iter!=m_filenameList.end(); ++iter) {
|
||||
// Read stats of files we create after we're done making them (execpt for this file, of course)
|
||||
DependFile* dfp = (DependFile*)&(*iter);
|
||||
|
|
@ -188,19 +188,19 @@ inline void V3FileDependImp::writeTimes(const string& filename, const string& cm
|
|||
if (dfp->filename() == filename) { showSize=0; showIno=0; } // We're writing it, so need to ignore it
|
||||
|
||||
*ofp<<(iter->target()?"T":"S")<<" ";
|
||||
*ofp<<" "<<setw(8)<<showSize;
|
||||
*ofp<<" "<<setw(8)<<showIno;
|
||||
*ofp<<" "<<setw(11)<<iter->cstime();
|
||||
*ofp<<" "<<setw(11)<<iter->cnstime();
|
||||
*ofp<<" "<<setw(11)<<iter->mstime();
|
||||
*ofp<<" "<<setw(11)<<iter->mnstime();
|
||||
*ofp<<" "<<std::setw(8)<<showSize;
|
||||
*ofp<<" "<<std::setw(8)<<showIno;
|
||||
*ofp<<" "<<std::setw(11)<<iter->cstime();
|
||||
*ofp<<" "<<std::setw(11)<<iter->cnstime();
|
||||
*ofp<<" "<<std::setw(11)<<iter->mstime();
|
||||
*ofp<<" "<<std::setw(11)<<iter->mnstime();
|
||||
*ofp<<" \""<<iter->filename()<<"\"";
|
||||
*ofp<<endl;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn) {
|
||||
const vl_unique_ptr<ifstream> ifp (V3File::new_ifstream_nodepend(filename));
|
||||
const vl_unique_ptr<std::ifstream> ifp (V3File::new_ifstream_nodepend(filename));
|
||||
if (ifp->fail()) {
|
||||
UINFO(2," --check-times failed: no input "<<filename<<endl);
|
||||
return false;
|
||||
|
|
@ -299,7 +299,7 @@ void V3File::createMakeDir() {
|
|||
// V3InFilterImp
|
||||
|
||||
class V3InFilterImp {
|
||||
typedef map<string,string> FileContentsMap;
|
||||
typedef std::map<string,string> FileContentsMap;
|
||||
typedef V3InFilter::StrList StrList;
|
||||
|
||||
FileContentsMap m_contentsMap; // Cache of file contents
|
||||
|
|
@ -316,11 +316,7 @@ class V3InFilterImp {
|
|||
|
||||
private:
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
bool readContents(const string& filename, StrList& outl) {
|
||||
if (m_pid) return readContentsFilter(filename,outl);
|
||||
|
|
|
|||
18
src/V3File.h
18
src/V3File.h
|
|
@ -34,23 +34,23 @@
|
|||
|
||||
class V3File {
|
||||
public:
|
||||
static ifstream* new_ifstream(const string& filename) {
|
||||
static std::ifstream* new_ifstream(const string& filename) {
|
||||
addSrcDepend(filename);
|
||||
return new_ifstream_nodepend (filename);
|
||||
}
|
||||
static ifstream* new_ifstream_nodepend(const string& filename) {
|
||||
return new ifstream(filename.c_str());
|
||||
static std::ifstream* new_ifstream_nodepend(const string& filename) {
|
||||
return new std::ifstream(filename.c_str());
|
||||
}
|
||||
static ofstream* new_ofstream(const string& filename, bool append=false) {
|
||||
static std::ofstream* new_ofstream(const string& filename, bool append=false) {
|
||||
addTgtDepend(filename);
|
||||
return new_ofstream_nodepend (filename, append);
|
||||
}
|
||||
static ofstream* new_ofstream_nodepend(const string& filename, bool append=false) {
|
||||
static std::ofstream* new_ofstream_nodepend(const string& filename, bool append=false) {
|
||||
if (filename != VL_DEV_NULL) createMakeDir();
|
||||
if (append) {
|
||||
return new ofstream(filename.c_str(), ios::app);
|
||||
return new std::ofstream(filename.c_str(), std::ios::app);
|
||||
} else {
|
||||
return new ofstream(filename.c_str());
|
||||
return new std::ofstream(filename.c_str());
|
||||
}
|
||||
}
|
||||
static FILE* new_fopen_w(const string& filename) {
|
||||
|
|
@ -78,7 +78,7 @@ class V3InFilterImp;
|
|||
class V3InFilter {
|
||||
public:
|
||||
// TYPES
|
||||
typedef list<string> StrList;
|
||||
typedef std::list<string> StrList;
|
||||
|
||||
private:
|
||||
V3InFilterImp* m_impp;
|
||||
|
|
@ -123,7 +123,7 @@ private:
|
|||
int m_nobreak; // Basic operator or begin paren, don't break next
|
||||
bool m_prependIndent;
|
||||
int m_indentLevel; // Current {} indentation
|
||||
stack<int> m_parenVec; // Stack of columns where last ( was
|
||||
std::stack<int> m_parenVec; // Stack of columns where last ( was
|
||||
|
||||
int endLevels(const char* strg);
|
||||
const char* indentStr(int levels);
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ int FileLineSingleton::nameToNumber(const string& filename) {
|
|||
//! Support XML output
|
||||
|
||||
//! Experimental. Updated to also put out the language.
|
||||
void FileLineSingleton::fileNameNumMapDumpXml(ostream& os) {
|
||||
void FileLineSingleton::fileNameNumMapDumpXml(std::ostream& os) {
|
||||
os<<"<files>\n";
|
||||
for (FileNameNumMap::const_iterator it = m_namemap.begin(); it != m_namemap.end(); ++it) {
|
||||
os<<"<file id=\""<<filenameLetters(it->second)
|
||||
|
|
@ -184,8 +184,8 @@ const string FileLine::profileFuncname() const {
|
|||
string FileLine::ascii() const {
|
||||
return filename()+":"+cvtToStr(lineno());
|
||||
}
|
||||
ostream& operator<<(ostream& os, FileLine* fileline) {
|
||||
os <<fileline->ascii()<<": "<<hex;
|
||||
std::ostream& operator<<(std::ostream& os, FileLine* fileline) {
|
||||
os <<fileline->ascii()<<": "<<std::hex;
|
||||
return(os);
|
||||
}
|
||||
|
||||
|
|
@ -237,9 +237,9 @@ void FileLine::modifyStateInherit(const FileLine* fromp) {
|
|||
}
|
||||
}
|
||||
|
||||
void FileLine::v3errorEnd(ostringstream& str) {
|
||||
void FileLine::v3errorEnd(std::ostringstream& str) {
|
||||
if (m_lineno) {
|
||||
ostringstream nsstr;
|
||||
std::ostringstream nsstr;
|
||||
nsstr<<this<<str.str();
|
||||
if (warnIsOff(V3Error::errorCode())) V3Error::suppressThisWarning();
|
||||
V3Error::v3errorEnd(nsstr);
|
||||
|
|
|
|||
|
|
@ -43,12 +43,12 @@ class FileLine;
|
|||
//! source file (each with its own unique filename number).
|
||||
class FileLineSingleton {
|
||||
// TYPES
|
||||
typedef map<string,int> FileNameNumMap;
|
||||
typedef map<string,V3LangCode> FileLangNumMap;
|
||||
typedef std::map<string,int> FileNameNumMap;
|
||||
typedef std::map<string,V3LangCode> FileLangNumMap;
|
||||
// MEMBERS
|
||||
FileNameNumMap m_namemap; // filenameno for each filename
|
||||
deque<string> m_names; // filename text for each filenameno
|
||||
deque<V3LangCode> m_languages; // language for each filenameno
|
||||
std::deque<string> m_names; // filename text for each filenameno
|
||||
std::deque<V3LangCode> m_languages; // language for each filenameno
|
||||
// COSNTRUCTORS
|
||||
FileLineSingleton() { }
|
||||
~FileLineSingleton() { }
|
||||
|
|
@ -60,7 +60,7 @@ protected:
|
|||
const V3LangCode numberToLang(int filenameno) const { return m_languages[filenameno]; }
|
||||
void numberToLang(int filenameno, const V3LangCode& l) { m_languages[filenameno] = l; }
|
||||
void clear() { m_namemap.clear(); m_names.clear(); m_languages.clear(); }
|
||||
void fileNameNumMapDumpXml(ostream& os);
|
||||
void fileNameNumMapDumpXml(std::ostream& os);
|
||||
static const string filenameLetters(int fileno);
|
||||
};
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ protected:
|
|||
class FileLine {
|
||||
int m_lineno;
|
||||
int m_filenameno;
|
||||
bitset<V3ErrorCode::_ENUM_MAX> m_warnOn;
|
||||
std::bitset<V3ErrorCode::_ENUM_MAX> m_warnOn;
|
||||
|
||||
private:
|
||||
struct EmptySecret {};
|
||||
|
|
@ -148,7 +148,7 @@ public:
|
|||
defaultFileLine().warnOff(code, flag); }
|
||||
static bool globalWarnOff(const string& code, bool flag) {
|
||||
return defaultFileLine().warnOff(code, flag); }
|
||||
static void fileNameNumMapDumpXml(ostream& os) {
|
||||
static void fileNameNumMapDumpXml(std::ostream& os) {
|
||||
singleton().fileNameNumMapDumpXml(os); }
|
||||
|
||||
// METHODS - Called from netlist
|
||||
|
|
@ -160,17 +160,17 @@ public:
|
|||
void modifyWarnOff(V3ErrorCode code, bool flag) { warnOff(code,flag); }
|
||||
|
||||
// OPERATORS
|
||||
void v3errorEnd(ostringstream& str);
|
||||
void v3errorEndFatal(ostringstream& str);
|
||||
void v3errorEnd(std::ostringstream& str);
|
||||
void v3errorEndFatal(std::ostringstream& str);
|
||||
string warnMore() const;
|
||||
inline bool operator==(FileLine rhs) const {
|
||||
return (m_lineno==rhs.m_lineno && m_filenameno==rhs.m_filenameno && m_warnOn==rhs.m_warnOn);
|
||||
}
|
||||
private:
|
||||
void v3errorEndFatalGuts(ostringstream& str);
|
||||
void v3errorEndFatalGuts(std::ostringstream& str);
|
||||
};
|
||||
ostream& operator<<(ostream& os, FileLine* fileline);
|
||||
std::ostream& operator<<(std::ostream& os, FileLine* fileline);
|
||||
|
||||
inline void FileLine::v3errorEndFatal(ostringstream& str) { v3errorEnd(str); assert(0); }
|
||||
inline void FileLine::v3errorEndFatal(std::ostringstream& str) { v3errorEnd(str); assert(0); }
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
137
src/V3Gate.cpp
137
src/V3Gate.cpp
|
|
@ -45,7 +45,7 @@
|
|||
#include "V3Stats.h"
|
||||
#include "V3Hashed.h"
|
||||
|
||||
typedef list<AstNodeVarRef*> GateVarRefList;
|
||||
typedef std::list<AstNodeVarRef*> GateVarRefList;
|
||||
|
||||
#define GATE_DEDUP_MAX_DEPTH 20
|
||||
|
||||
|
|
@ -53,11 +53,7 @@ typedef list<AstNodeVarRef*> GateVarRefList;
|
|||
|
||||
class GateBaseVisitor : public AstNVisitor {
|
||||
public:
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -176,6 +172,7 @@ public:
|
|||
// ACCESSORS
|
||||
virtual string name() const { return (cvtToStr((void*)m_nodep)+"@"+scopep()->prettyName()); }
|
||||
virtual string dotColor() const { return "yellow"; }
|
||||
virtual FileLine* fileline() const { return nodep()->fileline(); }
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
AstActive* activep() const { return m_activep; }
|
||||
bool slow() const { return m_slow; }
|
||||
|
|
@ -207,7 +204,7 @@ private:
|
|||
// VISITORS
|
||||
virtual void visit(AstNodeVarRef* nodep) {
|
||||
++m_ops;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// We only allow a LHS ref for the var being set, and a RHS ref for something else being read.
|
||||
if (nodep->varScopep()->varp()->isSc()) {
|
||||
clearSimple("SystemC sig"); // Don't want to eliminate the VL_ASSIGN_SI's
|
||||
|
|
@ -234,19 +231,19 @@ private:
|
|||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
m_substTreep = nodep->rhsp();
|
||||
if (!nodep->lhsp()->castNodeVarRef())
|
||||
if (!VN_IS(nodep->lhsp(), NodeVarRef))
|
||||
clearSimple("ASSIGN(non-VARREF)");
|
||||
else nodep->iterateChildren(*this);
|
||||
else iterateChildren(nodep);
|
||||
// We don't push logic other then assignments/NOTs into SenItems
|
||||
// This avoids a mess in computing what exactly a POSEDGE is
|
||||
// V3Const cleans up any NOTs by flipping the edges for us
|
||||
if (m_buffersOnly
|
||||
&& !(nodep->rhsp()->castVarRef()
|
||||
&& !(VN_IS(nodep->rhsp(), VarRef)
|
||||
// Avoid making non-clocked logic into clocked,
|
||||
// as it slows down the verilator_sim_benchmark
|
||||
|| (nodep->rhsp()->castNot()
|
||||
&& nodep->rhsp()->castNot()->lhsp()->castVarRef()
|
||||
&& nodep->rhsp()->castNot()->lhsp()->castVarRef()->varp()->isUsedClock())
|
||||
|| (VN_IS(nodep->rhsp(), Not)
|
||||
&& VN_IS(VN_CAST(nodep->rhsp(), Not)->lhsp(), VarRef)
|
||||
&& VN_CAST(VN_CAST(nodep->rhsp(), Not)->lhsp(), VarRef)->varp()->isUsedClock())
|
||||
)) {
|
||||
clearSimple("Not a buffer (goes to a clock)");
|
||||
}
|
||||
|
|
@ -265,7 +262,7 @@ private:
|
|||
UINFO(5, "Non optimizable type: "<<nodep<<endl);
|
||||
clearSimple("Non optimizable type");
|
||||
}
|
||||
else nodep->iterateChildren(*this);
|
||||
else iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
|
|
@ -277,7 +274,7 @@ public:
|
|||
m_dedupe = dedupe;
|
||||
m_ops = 0;
|
||||
// Iterate
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
// Check results
|
||||
if (!m_substTreep) {
|
||||
clearSimple("No assignment found\n");
|
||||
|
|
@ -342,8 +339,8 @@ private:
|
|||
m_logicVertexp->clearReducible("Block Unreducible"); // Sequential logic is dedupable
|
||||
}
|
||||
if (consumeReason) m_logicVertexp->setConsumed(consumeReason);
|
||||
if (nodep->castSenItem()) m_logicVertexp->setConsumed("senItem");
|
||||
nodep->iterateChildren(*this);
|
||||
if (VN_IS(nodep, SenItem)) m_logicVertexp->setConsumed("senItem");
|
||||
iterateChildren(nodep);
|
||||
m_logicVertexp = NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -384,7 +381,7 @@ private:
|
|||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
//if (debug()>6) m_graph.dump();
|
||||
if (debug()>6) m_graph.dumpDotFilePrefixed("gate_pre");
|
||||
warnSignals(); // Before loss of sync/async pointers
|
||||
|
|
@ -411,14 +408,14 @@ private:
|
|||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
m_activeReducible = true;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
UINFO(4," SCOPE "<<nodep<<endl);
|
||||
m_scopep = nodep;
|
||||
m_logicVertexp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_scopep = NULL;
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
|
|
@ -427,7 +424,7 @@ private:
|
|||
m_activeReducible = !(nodep->hasClocked()); // Seq logic outputs aren't reducible
|
||||
m_activep = nodep;
|
||||
AstNode::user2ClearTree();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
AstNode::user2ClearTree();
|
||||
m_activep = NULL;
|
||||
m_activeReducible = true;
|
||||
|
|
@ -475,7 +472,7 @@ private:
|
|||
// The gating term of a AstSenGate is normal logic
|
||||
m_inSenItem = true;
|
||||
if (m_logicVertexp) { // Already under logic; presumably a SenGate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
} else { // Standalone item, probably right under a SenTree
|
||||
iterateNewStmt(nodep, NULL, NULL);
|
||||
}
|
||||
|
|
@ -508,16 +505,16 @@ private:
|
|||
m_inSlow = lastslow;
|
||||
}
|
||||
virtual void visit(AstConcat* nodep) {
|
||||
if (nodep->backp()->castNodeAssign() && nodep->backp()->castNodeAssign()->lhsp()==nodep) {
|
||||
if (VN_IS(nodep->backp(), NodeAssign) && VN_CAST(nodep->backp(), NodeAssign)->lhsp()==nodep) {
|
||||
nodep->v3fatalSrc("Concat on LHS of assignment; V3Const should have deleted it");
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->isOutputter() && m_logicVertexp) m_logicVertexp->setConsumed("outputter");
|
||||
}
|
||||
|
||||
|
|
@ -532,7 +529,7 @@ public:
|
|||
m_activeReducible = true;
|
||||
m_inSenItem = false;
|
||||
m_inSlow = false;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~GateVisitor() {
|
||||
V3Stats::addStat("Optimizations, Gate sigs deleted", m_statSigs);
|
||||
|
|
@ -702,33 +699,33 @@ void GateVisitor::replaceAssigns() {
|
|||
// Take the Comments/assigns that were moved to the VarScope and change them to a
|
||||
// simple value assignment
|
||||
AstVarScope* vscp = vvertexp->varScp();
|
||||
if (vscp->valuep() && !vscp->valuep()->castNodeMath()) {
|
||||
if (vscp->valuep() && !VN_IS(vscp->valuep(), NodeMath)) {
|
||||
//if (debug()>9) vscp->dumpTree(cout, "-vscPre: ");
|
||||
while (AstNode* delp=vscp->valuep()->castComment()) {
|
||||
while (AstNode* delp=VN_CAST(vscp->valuep(), Comment)) {
|
||||
delp->unlinkFrBack()->deleteTree(); VL_DANGLING(delp);
|
||||
}
|
||||
if (AstInitial* delp=vscp->valuep()->castInitial()) {
|
||||
if (AstInitial* delp=VN_CAST(vscp->valuep(), Initial)) {
|
||||
AstNode* bodyp=delp->bodysp();
|
||||
bodyp->unlinkFrBackWithNext();
|
||||
delp->replaceWith(bodyp);
|
||||
delp->deleteTree(); VL_DANGLING(delp);
|
||||
}
|
||||
if (AstAlways* delp=vscp->valuep()->castAlways()) {
|
||||
if (AstAlways* delp=VN_CAST(vscp->valuep(), Always)) {
|
||||
AstNode* bodyp=delp->bodysp();
|
||||
bodyp->unlinkFrBackWithNext();
|
||||
delp->replaceWith(bodyp);
|
||||
delp->deleteTree(); VL_DANGLING(delp);
|
||||
}
|
||||
if (AstNodeAssign* delp=vscp->valuep()->castNodeAssign()) {
|
||||
if (AstNodeAssign* delp=VN_CAST(vscp->valuep(), NodeAssign)) {
|
||||
AstNode* rhsp=delp->rhsp();
|
||||
rhsp->unlinkFrBack();
|
||||
delp->replaceWith(rhsp);
|
||||
delp->deleteTree(); VL_DANGLING(delp);
|
||||
}
|
||||
//if (debug()>9) {vscp->dumpTree(cout, "-vscDone: "); cout<<endl;}
|
||||
if (!vscp->valuep()->castNodeMath()
|
||||
if (!VN_IS(vscp->valuep(), NodeMath)
|
||||
|| vscp->valuep()->nextp()) {
|
||||
vscp->dumpTree(cerr, "vscStrange: ");
|
||||
vscp->dumpTree(std::cerr, "vscStrange: ");
|
||||
vscp->v3fatalSrc("Value of varscope not mathematical");
|
||||
}
|
||||
}
|
||||
|
|
@ -829,8 +826,8 @@ private:
|
|||
m_didReplace = true;
|
||||
if (nodep->lvalue()) nodep->v3fatalSrc("Can't replace lvalue assignments with const var");
|
||||
AstNode* substp = m_replaceTreep->cloneTree(false);
|
||||
if (nodep->castNodeVarRef()
|
||||
&& substp->castNodeVarRef()
|
||||
if (VN_IS(nodep, NodeVarRef)
|
||||
&& VN_IS(substp, NodeVarRef)
|
||||
&& nodep->same(substp)) {
|
||||
// Prevent a infinite loop...
|
||||
substp->v3fatalSrc("Replacing node with itself; perhaps circular logic?");
|
||||
|
|
@ -840,15 +837,15 @@ private:
|
|||
// IE what we're replacing with.
|
||||
// However a VARREF should point to the original as it's otherwise confusing
|
||||
// to throw warnings that point to a PIN rather than where the pin us used.
|
||||
if (substp->castVarRef()) substp->fileline(nodep->fileline());
|
||||
if (VN_IS(substp, VarRef)) substp->fileline(nodep->fileline());
|
||||
// Make the substp an rvalue like nodep. This facilitate the hashing in dedupe.
|
||||
if (AstNodeVarRef* varrefp = substp->castNodeVarRef()) varrefp->lvalue(false);
|
||||
if (AstNodeVarRef* varrefp = VN_CAST(substp, NodeVarRef)) varrefp->lvalue(false);
|
||||
nodep->replaceWith(substp);
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
|
|
@ -857,7 +854,7 @@ public:
|
|||
m_didReplace = false;
|
||||
m_elimVarScp = varscp;
|
||||
m_replaceTreep = replaceTreep;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
bool didReplace() const { return m_didReplace; }
|
||||
};
|
||||
|
|
@ -929,7 +926,7 @@ public:
|
|||
// So dupit is either a different, duplicate rhsp, or the end of the hash.
|
||||
if (dupit != m_hashed.end()) {
|
||||
m_hashed.erase(inserted);
|
||||
return m_hashed.iteratorNodep(dupit)->user2p()->castNodeAssign();
|
||||
return VN_CAST(m_hashed.iteratorNodep(dupit)->user2p(), NodeAssign);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -971,7 +968,7 @@ private:
|
|||
if (m_dedupable) {
|
||||
if (!m_always) {
|
||||
m_always = true;
|
||||
alwaysp->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(alwaysp->bodysp());
|
||||
} else {
|
||||
m_dedupable = false;
|
||||
}
|
||||
|
|
@ -985,7 +982,7 @@ private:
|
|||
if (m_dedupable) {
|
||||
if (m_always && !m_ifCondp && !ifp->elsesp()) { //we're under an always, this is the first IF, and there's no else
|
||||
m_ifCondp = ifp->condp();
|
||||
ifp->ifsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(ifp->ifsp());
|
||||
} else {
|
||||
m_dedupable = false;
|
||||
}
|
||||
|
|
@ -1013,11 +1010,11 @@ public:
|
|||
m_ifCondp = NULL;
|
||||
m_always = false;
|
||||
m_dedupable = true;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
if (m_dedupable && m_assignp) {
|
||||
AstNode* lhsp = m_assignp->lhsp();
|
||||
// Possible todo, handle more complex lhs expressions
|
||||
if (AstNodeVarRef* lhsVarRefp = lhsp->castNodeVarRef()) {
|
||||
if (AstNodeVarRef* lhsVarRefp = VN_CAST(lhsp, NodeVarRef)) {
|
||||
if (lhsVarRefp->varScopep() != consumerVarScopep) consumerVarScopep->v3fatalSrc("Consumer doesn't match lhs of assign");
|
||||
if (AstNodeAssign* dup = m_hash.hashAndFindDupe(m_assignp,activep,m_ifCondp)) {
|
||||
return (AstNodeVarRef*) dup->lhsp();
|
||||
|
|
@ -1157,13 +1154,13 @@ private:
|
|||
|
||||
// assemble two Sel into one if possible
|
||||
AstSel* merge(AstSel* pre, AstSel* cur) {
|
||||
AstVarRef* preVarRefp = pre->fromp()->castVarRef();
|
||||
AstVarRef* curVarRefp = cur->fromp()->castVarRef();
|
||||
AstVarRef* preVarRefp = VN_CAST(pre->fromp(), VarRef);
|
||||
AstVarRef* curVarRefp = VN_CAST(cur->fromp(), VarRef);
|
||||
if (!preVarRefp || !curVarRefp || !curVarRefp->same(preVarRefp)) return NULL; // not the same var
|
||||
AstConst* pstart = pre->lsbp()->castConst();
|
||||
AstConst* pwidth = pre->widthp()->castConst();
|
||||
AstConst* cstart = cur->lsbp()->castConst();
|
||||
AstConst* cwidth = cur->widthp()->castConst();
|
||||
const AstConst* pstart = VN_CAST(pre->lsbp(), Const);
|
||||
const AstConst* pwidth = VN_CAST(pre->widthp(), Const);
|
||||
const AstConst* cstart = VN_CAST(cur->lsbp(), Const);
|
||||
const AstConst* cwidth = VN_CAST(cur->widthp(), Const);
|
||||
if (!pstart || !pwidth || !cstart || !cwidth) return NULL; // too complicated
|
||||
if (cur->lsbConst()+cur->widthConst() == pre->lsbConst())
|
||||
return new AstSel(curVarRefp->fileline(), curVarRefp->cloneTree(false), cur->lsbConst(), pre->widthConst()+cur->widthConst());
|
||||
|
|
@ -1175,10 +1172,10 @@ private:
|
|||
V3GraphEdge* oldedgep = edgep;
|
||||
edgep = edgep->inNextp(); // for recursive since the edge could be deleted
|
||||
if (GateLogicVertex* lvertexp = dynamic_cast<GateLogicVertex*>(oldedgep->fromp())) {
|
||||
if (AstNodeAssign* assignp = lvertexp->nodep()->castNodeAssign()) {
|
||||
//if (lvertexp->outSize1() && assignp->lhsp()->castSel()) {
|
||||
if (assignp->lhsp()->castSel() && lvertexp->outSize1()) {
|
||||
UINFO(9, "assing to the nodep["<<assignp->lhsp()->castSel()->lsbConst()<<"]"<<endl);
|
||||
if (AstNodeAssign* assignp = VN_CAST(lvertexp->nodep(), NodeAssign)) {
|
||||
//if (lvertexp->outSize1() && VN_IS(assignp->lhsp(), Sel)) {
|
||||
if (VN_IS(assignp->lhsp(), Sel) && lvertexp->outSize1()) {
|
||||
UINFO(9, "assing to the nodep["<<VN_CAST(assignp->lhsp(), Sel)->lsbConst()<<"]"<<endl);
|
||||
// first assign with Sel-lhs
|
||||
if (!m_activep) m_activep = lvertexp->activep();
|
||||
if (!m_logicvp) m_logicvp = lvertexp;
|
||||
|
|
@ -1192,8 +1189,8 @@ private:
|
|||
continue;
|
||||
}
|
||||
|
||||
AstSel* preselp = m_assignp->lhsp()->castSel();
|
||||
AstSel* curselp = assignp->lhsp()->castSel();
|
||||
AstSel* preselp = VN_CAST(m_assignp->lhsp(), Sel);
|
||||
AstSel* curselp = VN_CAST(assignp->lhsp(), Sel);
|
||||
if (!preselp || !curselp) continue;
|
||||
|
||||
if (AstSel* newselp = merge(preselp, curselp)) {
|
||||
|
|
@ -1292,13 +1289,13 @@ private:
|
|||
}
|
||||
virtual void visit(AstConcat* nodep) {
|
||||
UINFO(9,"CLK DECOMP Concat search (off = "<<m_offset<<") - "<<nodep<<endl);
|
||||
nodep->rhsp()->iterate(*this);
|
||||
nodep->lhsp()->iterate(*this);
|
||||
iterate(nodep->rhsp());
|
||||
iterate(nodep->lhsp());
|
||||
}
|
||||
//--------------------
|
||||
// Default
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
|
|
@ -1315,7 +1312,7 @@ public:
|
|||
m_offset = 0;
|
||||
m_found = false;
|
||||
// Iterate
|
||||
concatp->accept(*this);
|
||||
iterate(concatp);
|
||||
UINFO(9,"CLK DECOMP Concat Offset (found = "<<m_found<<") ("<<m_found_offset<<") - "<<concatp<<" : "<<vscp<<endl);
|
||||
offsetr = m_found_offset;
|
||||
return m_found;
|
||||
|
|
@ -1370,10 +1367,10 @@ private:
|
|||
virtual VNUser visit(GateLogicVertex* lvertexp, VNUser vu) {
|
||||
GateClkDecompState* currState = (GateClkDecompState*) vu.c();
|
||||
int clk_offset = currState->m_offset;
|
||||
if (AstAssignW* assignp = lvertexp->nodep()->castAssignW()) {
|
||||
if (const AstAssignW* assignp = VN_CAST(lvertexp->nodep(), AssignW)) {
|
||||
UINFO(9,"CLK DECOMP Logic (off = "<<clk_offset<<") - "<<lvertexp<<" : "<<m_clk_vsp<<endl);
|
||||
if (AstSel* rselp = assignp->rhsp()->castSel()) {
|
||||
if (rselp->lsbp()->castConst() && rselp->widthp()->castConst()) {
|
||||
if (AstSel* rselp = VN_CAST(assignp->rhsp(), Sel)) {
|
||||
if (VN_IS(rselp->lsbp(), Const) && VN_IS(rselp->widthp(), Const)) {
|
||||
if (clk_offset < rselp->lsbConst() || clk_offset > rselp->msbConst()) {
|
||||
UINFO(9,"CLK DECOMP Sel [ "<<rselp->msbConst()<<" : "<<rselp->lsbConst()<<" ] dropped clock ("<<clk_offset<<")"<<endl);
|
||||
return VNUser(0);
|
||||
|
|
@ -1382,7 +1379,7 @@ private:
|
|||
} else {
|
||||
return VNUser(0);
|
||||
}
|
||||
} else if (AstConcat* catp = assignp->rhsp()->castConcat()) {
|
||||
} else if (AstConcat* catp = VN_CAST(assignp->rhsp(), Concat)) {
|
||||
UINFO(9,"CLK DECOMP Concat searching - "<<assignp->lhsp()<<endl);
|
||||
int concat_offset;
|
||||
if (!m_concat_visitor.concatOffset(catp, currState->m_last_vsp, concat_offset)) {
|
||||
|
|
@ -1390,13 +1387,13 @@ private:
|
|||
}
|
||||
clk_offset += concat_offset;
|
||||
}
|
||||
if (AstSel* lselp = assignp->lhsp()->castSel()) {
|
||||
if (lselp->lsbp()->castConst() && lselp->widthp()->castConst()) {
|
||||
if (const AstSel* lselp = VN_CAST(assignp->lhsp(), Sel)) {
|
||||
if (VN_IS(lselp->lsbp(), Const) && VN_IS(lselp->widthp(), Const)) {
|
||||
clk_offset += lselp->lsbConst();
|
||||
} else {
|
||||
return VNUser(0);
|
||||
}
|
||||
} else if (AstVarRef* vrp = assignp->lhsp()->castVarRef()) {
|
||||
} else if (const AstVarRef* vrp = VN_CAST(assignp->lhsp(), VarRef)) {
|
||||
if (vrp->dtypep()->width() == 1 && m_seen_clk_vectors) {
|
||||
if (clk_offset != 0) {
|
||||
UINFO(9,"Should only make it here with clk_offset = 0"<<endl);
|
||||
|
|
@ -1468,7 +1465,7 @@ class GateDeassignVisitor : public GateBaseVisitor {
|
|||
private:
|
||||
// VISITORS
|
||||
virtual void visit(AstVarScope* nodep) {
|
||||
if (AstNodeAssign* assp = nodep->valuep()->castNodeAssign()) {
|
||||
if (AstNodeAssign* assp = VN_CAST(nodep->valuep(), NodeAssign)) {
|
||||
UINFO(5," Removeassign "<<assp<<endl);
|
||||
AstNode* valuep = assp->rhsp();
|
||||
valuep->unlinkFrBack();
|
||||
|
|
@ -1480,13 +1477,13 @@ private:
|
|||
virtual void visit(AstVar* nodep) {}
|
||||
virtual void visit(AstActive* nodep) {}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit GateDeassignVisitor(AstNode* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~GateDeassignVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,11 +38,7 @@
|
|||
|
||||
class GenClkBaseVisitor : public AstNVisitor {
|
||||
protected:
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -65,7 +61,7 @@ private:
|
|||
// METHODS
|
||||
AstVarScope* genInpClk(AstVarScope* vscp) {
|
||||
if (vscp->user2p()) {
|
||||
return vscp->user2p()->castVarScope();
|
||||
return VN_CAST(vscp->user2p(), VarScope);
|
||||
} else {
|
||||
AstVar* varp = vscp->varp();
|
||||
string newvarname = "__VinpClk__"+vscp->scopep()->nameDotless()+"__"+varp->name();
|
||||
|
|
@ -94,7 +90,7 @@ private:
|
|||
if (!scopep) nodep->v3fatalSrc("No scope found on top level");
|
||||
m_scopetopp = scopep;
|
||||
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//----
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
|
|
@ -115,17 +111,18 @@ private:
|
|||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
m_activep = nodep;
|
||||
nodep->sensesp()->iterateChildren(*this); // iterateAndNext?
|
||||
if (!nodep->sensesp()) nodep->v3fatalSrc("Unlinked");
|
||||
iterateChildren(nodep->sensesp()); // iterateAndNext?
|
||||
m_activep = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
//-----
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -133,7 +130,7 @@ public:
|
|||
m_topModp = topModp;
|
||||
m_scopetopp = NULL;
|
||||
m_activep = NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~GenClkRenameVisitor() {}
|
||||
};
|
||||
|
|
@ -157,7 +154,7 @@ private:
|
|||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
AstNode::user1ClearTree(); // user1p() used on entire tree
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
{
|
||||
// Make the new clock signals and replace any activate references
|
||||
// See rename, it does some AstNode::userClearTree()'s
|
||||
|
|
@ -168,15 +165,15 @@ private:
|
|||
// Only track the top scopes, not lower level functions
|
||||
if (nodep->isTop()) {
|
||||
m_topModp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->funcp()->entryPoint()) {
|
||||
// Enter the function and trace it
|
||||
m_tracingCall = true;
|
||||
nodep->funcp()->accept(*this);
|
||||
iterate(nodep->funcp());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
|
|
@ -187,7 +184,7 @@ private:
|
|||
return;
|
||||
}
|
||||
m_tracingCall = false;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//----
|
||||
|
||||
|
|
@ -209,21 +206,22 @@ private:
|
|||
virtual void visit(AstNodeAssign* nodep) {
|
||||
//UINFO(8,"ASS "<<nodep<<endl);
|
||||
m_assignp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_assignp = NULL;
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
UINFO(8,"ACTIVE "<<nodep<<endl);
|
||||
m_activep = nodep;
|
||||
nodep->sensesp()->iterateChildren(*this); // iterateAndNext?
|
||||
if (!nodep->sensesp()) nodep->v3fatalSrc("Unlinked");
|
||||
iterateChildren(nodep->sensesp()); // iterateAndNext?
|
||||
m_activep = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
//-----
|
||||
virtual void visit(AstVar*) {} // Don't want varrefs under it
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -232,7 +230,7 @@ public:
|
|||
, m_tracingCall(false)
|
||||
, m_assignp(NULL)
|
||||
, m_topModp(NULL) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~GenClkReadVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
#include "V3Graph.h"
|
||||
|
||||
int V3Graph::s_debug = 0;
|
||||
int V3Graph::debug() { return max(V3Error::debugDefault(), s_debug); }
|
||||
int V3Graph::debug() { return std::max(V3Error::debugDefault(), s_debug); }
|
||||
|
||||
//######################################################################
|
||||
//######################################################################
|
||||
|
|
@ -82,7 +82,7 @@ void V3GraphVertex::rerouteEdges(V3Graph* graphp) {
|
|||
for (V3GraphEdge* iedgep = inBeginp(); iedgep; iedgep=iedgep->inNextp()) {
|
||||
for (V3GraphEdge* oedgep = outBeginp(); oedgep; oedgep=oedgep->outNextp()) {
|
||||
new V3GraphEdge (graphp, iedgep->fromp(), oedgep->top(),
|
||||
min(iedgep->weight(),oedgep->weight()),
|
||||
std::min(iedgep->weight(),oedgep->weight()),
|
||||
iedgep->cutable() && oedgep->cutable());
|
||||
}
|
||||
}
|
||||
|
|
@ -118,7 +118,41 @@ uint32_t V3GraphVertex::outHash() const {
|
|||
return hash;
|
||||
}
|
||||
|
||||
ostream& operator<<(ostream& os, V3GraphVertex* vertexp) {
|
||||
V3GraphEdge* V3GraphVertex::findConnectingEdgep(GraphWay way,
|
||||
const V3GraphVertex* waywardp) {
|
||||
// O(edges) linear search. Searches search both nodes' edge lists in
|
||||
// parallel. The lists probably aren't _both_ huge, so this is
|
||||
// unlikely to blow up even on fairly nasty graphs.
|
||||
GraphWay inv = way.invert();
|
||||
V3GraphEdge* aedgep = this->beginp(way);
|
||||
V3GraphEdge* bedgep = waywardp->beginp(inv);
|
||||
while (aedgep && bedgep) {
|
||||
if (aedgep->furtherp(way) == waywardp) return aedgep;
|
||||
if (bedgep->furtherp(inv) == this) return bedgep;
|
||||
aedgep = aedgep->nextp(way);
|
||||
bedgep = bedgep->nextp(inv);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void V3GraphVertex::v3errorEnd(std::ostringstream& str) const {
|
||||
std::ostringstream nsstr;
|
||||
nsstr<<str.str();
|
||||
if (debug()) {
|
||||
nsstr<<endl;
|
||||
nsstr<<"-vertex: "<<this<<endl;
|
||||
}
|
||||
if (!fileline()) {
|
||||
V3Error::v3errorEnd(nsstr);
|
||||
} else {
|
||||
fileline()->v3errorEnd(nsstr);
|
||||
}
|
||||
}
|
||||
void V3GraphVertex::v3errorEndFatal(std::ostringstream& str) const {
|
||||
v3errorEnd(str); assert(0);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp) {
|
||||
os<<" VERTEX="<<vertexp->name();
|
||||
if (vertexp->rank()) os<<" r"<<vertexp->rank();
|
||||
if (vertexp->fanout()!=0.0) os<<" f"<<vertexp->fanout();
|
||||
|
|
@ -239,12 +273,16 @@ void V3Graph::clearColors() {
|
|||
//======================================================================
|
||||
// Dumping
|
||||
|
||||
void V3Graph::loopsVertexCb(V3GraphVertex* vertexp) {
|
||||
// Needed here as V3GraphVertex<< isn't defined until later in header
|
||||
cerr<<"-Info-Loop: "<<(void*)(vertexp)<<" "<<vertexp<<endl;
|
||||
void V3Graph::loopsMessageCb(V3GraphVertex* vertexp) {
|
||||
vertexp->v3fatalSrc("Loops detected in graph: "<<vertexp);
|
||||
}
|
||||
|
||||
void V3Graph::dump(ostream& os) {
|
||||
void V3Graph::loopsVertexCb(V3GraphVertex* vertexp) {
|
||||
// Needed here as V3GraphVertex<< isn't defined until later in header
|
||||
std::cerr<<"-Info-Loop: "<<(void*)(vertexp)<<" "<<vertexp<<endl;
|
||||
}
|
||||
|
||||
void V3Graph::dump(std::ostream& os) {
|
||||
// This generates a file used by graphviz, http://www.graphviz.org
|
||||
os<<" Graph:\n";
|
||||
// Print vertices
|
||||
|
|
@ -262,7 +300,7 @@ void V3Graph::dump(ostream& os) {
|
|||
}
|
||||
}
|
||||
|
||||
void V3Graph::dumpEdge(ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep) {
|
||||
void V3Graph::dumpEdge(std::ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep) {
|
||||
if (edgep->weight()
|
||||
&& (edgep->fromp() == vertexp
|
||||
|| edgep->top() == vertexp)) {
|
||||
|
|
@ -299,7 +337,7 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
|
|||
*logp<<"\t\t rankdir="<<dotRankDir()<<"];\n";
|
||||
|
||||
// List of all possible subgraphs
|
||||
typedef multimap<string,V3GraphVertex*> SubgraphMmap;
|
||||
typedef std::multimap<string,V3GraphVertex*> SubgraphMmap;
|
||||
SubgraphMmap subgraphs;
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
string vertexSubgraph = (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : "";
|
||||
|
|
@ -308,7 +346,7 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
|
|||
|
||||
// We use a map here, as we don't want to corrupt anything (userp) in the graph,
|
||||
// and we don't care if this is slow.
|
||||
map<V3GraphVertex*,int> numMap;
|
||||
std::map<V3GraphVertex*,int> numMap;
|
||||
|
||||
// Print vertices
|
||||
int n=0;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
class FileLine;
|
||||
class V3Graph;
|
||||
class V3GraphVertex;
|
||||
class V3GraphEdge;
|
||||
|
|
@ -40,6 +41,38 @@ class OrderLogicVertex;
|
|||
|
||||
typedef bool (*V3EdgeFuncP)(const V3GraphEdge* edgep);
|
||||
|
||||
//=============================================================================
|
||||
// When the Graph represents a directional acyclical graph (DAG), following
|
||||
// the to() edges is forward, and back() is reverse. However, sometimes
|
||||
// it's useful to have algorithms that can walk in either direction, hence
|
||||
// some methods take GraphWay to programmatically select the direction.
|
||||
|
||||
class GraphWay {
|
||||
public:
|
||||
enum en { FORWARD=0,
|
||||
REVERSE=1,
|
||||
NUM_WAYS=2 // NUM_WAYS is not an actual way, it's typically
|
||||
// // an array dimension or loop bound.
|
||||
};
|
||||
enum en m_e;
|
||||
inline GraphWay() : m_e(FORWARD) {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline GraphWay(en _e) : m_e(_e) {}
|
||||
explicit inline GraphWay(int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = { "FORWARD", "REVERSE" };
|
||||
return names[m_e];
|
||||
};
|
||||
// METHODS unique to this class
|
||||
GraphWay invert() const { return m_e == FORWARD ? REVERSE : FORWARD; }
|
||||
bool forward() const { return m_e == FORWARD; }
|
||||
bool reverse() const { return m_e != FORWARD; }
|
||||
};
|
||||
inline bool operator==(GraphWay lhs, GraphWay rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator==(GraphWay lhs, GraphWay::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator==(GraphWay::en lhs, GraphWay rhs) { return (lhs == rhs.m_e); }
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Graph {
|
||||
|
|
@ -56,7 +89,7 @@ protected:
|
|||
void acyclicCut();
|
||||
void acyclicLoop(V3GraphVertex* vertexp, int depth);
|
||||
double orderDFSIterate(V3GraphVertex* vertexp);
|
||||
void dumpEdge(ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep);
|
||||
void dumpEdge(std::ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep);
|
||||
void verticesUnlink() { m_vertices.reset(); }
|
||||
// ACCESSORS
|
||||
static int debug();
|
||||
|
|
@ -101,8 +134,13 @@ public:
|
|||
/// Order all vertices by rank and fanout, lowest first
|
||||
/// Sort all vertices by rank and fanout, lowest first
|
||||
/// Sort all edges by weight, lowest first
|
||||
/// Side-effect: assigns ranks to every node.
|
||||
void order();
|
||||
|
||||
// Similar to order() but does not assign ranks. Caller must
|
||||
// ensure that the graph has been ranked ahead of the call.
|
||||
void orderPreRanked();
|
||||
|
||||
/// Make acyclical (into a tree) by breaking a minimal subset of cutable edges.
|
||||
void acyclic(V3EdgeFuncP edgeFuncp);
|
||||
|
||||
|
|
@ -118,6 +156,12 @@ public:
|
|||
/// Remove any redundant edges, weights become SUM of any other weight
|
||||
void removeRedundantEdgesSum(V3EdgeFuncP edgeFuncp);
|
||||
|
||||
/// Remove any transitive edges. E.g. if have edges A->B, B->C, and A->C
|
||||
/// then A->C is a "transitive" edge; it's implied by the first two
|
||||
/// (assuming the DAG is a dependency graph.)
|
||||
/// This algorithm can be expensive.
|
||||
void removeTransitiveEdges();
|
||||
|
||||
/// Call loopsVertexCb on any one loop starting where specified
|
||||
void reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp);
|
||||
|
||||
|
|
@ -125,7 +169,7 @@ public:
|
|||
void subtreeLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp, V3Graph* loopGraphp);
|
||||
|
||||
/// Debugging
|
||||
void dump(ostream& os=cout);
|
||||
void dump(std::ostream& os=std::cout);
|
||||
void dumpDotFile(const string& filename, bool colorAsSubgraph) const;
|
||||
void dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph=false) const;
|
||||
void dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsSubgraph=false) const;
|
||||
|
|
@ -134,7 +178,7 @@ public:
|
|||
static void selfTest();
|
||||
|
||||
// CALLBACKS
|
||||
virtual void loopsMessageCb(V3GraphVertex* vertexp) { v3fatalSrc("Loops detected in graph: "<<vertexp); }
|
||||
virtual void loopsMessageCb(V3GraphVertex* vertexp);
|
||||
virtual void loopsVertexCb(V3GraphVertex* vertexp);
|
||||
};
|
||||
|
||||
|
|
@ -180,6 +224,7 @@ public:
|
|||
virtual string dotStyle() const { return ""; }
|
||||
virtual string dotName() const { return ""; }
|
||||
virtual uint32_t rankAdder() const { return 1; }
|
||||
virtual FileLine* fileline() const { return NULL; } // NULL for unknown
|
||||
virtual int sortCmp(const V3GraphVertex* rhsp) const {
|
||||
// LHS goes first if of lower rank, or lower fanout
|
||||
if (m_rank < rhsp->m_rank) return -1;
|
||||
|
|
@ -207,11 +252,20 @@ public:
|
|||
bool outEmpty() const { return outBeginp()==NULL; }
|
||||
bool outSize1() const;
|
||||
uint32_t outHash() const;
|
||||
V3GraphEdge* beginp(GraphWay way) const {
|
||||
return way.forward() ? outBeginp() : inBeginp(); }
|
||||
// METHODS
|
||||
void rerouteEdges(V3Graph* graphp); ///< Edges are routed around this vertex to point from "from" directly to "to"
|
||||
/// Error reporting
|
||||
void v3errorEnd(std::ostringstream& str) const;
|
||||
void v3errorEndFatal(std::ostringstream& str) const;
|
||||
/// Edges are routed around this vertex to point from "from" directly to "to"
|
||||
void rerouteEdges(V3Graph* graphp);
|
||||
/// Find the edge connecting ap and bp, where bp is wayward from ap.
|
||||
/// If edge is not found returns NULL. O(edges) performance.
|
||||
V3GraphEdge* findConnectingEdgep(GraphWay way, const V3GraphVertex* waywardp);
|
||||
};
|
||||
|
||||
ostream& operator<<(ostream& os, V3GraphVertex* vertexp);
|
||||
std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp);
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
|
@ -273,14 +327,18 @@ public:
|
|||
void* userp() const { return m_userp; }
|
||||
void user(uint32_t user) { m_user = user; }
|
||||
uint32_t user() const { return m_user; }
|
||||
V3GraphVertex* fromp() const { return m_fromp; }
|
||||
V3GraphVertex* top() const { return m_top; }
|
||||
V3GraphVertex* fromp() const { return m_fromp; }
|
||||
V3GraphVertex* top() const { return m_top; }
|
||||
V3GraphVertex* closerp(GraphWay way) const { return way.forward() ? fromp() : top(); }
|
||||
V3GraphVertex* furtherp(GraphWay way) const { return way.forward() ? top() : fromp(); }
|
||||
// STATIC ACCESSORS
|
||||
static bool followNotCutable(const V3GraphEdge* edgep) { return !edgep->m_cutable; }
|
||||
static bool followAlwaysTrue(const V3GraphEdge*) { return true; }
|
||||
static bool followNotCutable(const V3GraphEdge* edgep) { return !edgep->m_cutable; }
|
||||
static bool followAlwaysTrue(const V3GraphEdge*) { return true; }
|
||||
// ITERATORS
|
||||
V3GraphEdge* outNextp() const { return m_outs.nextp(); }
|
||||
V3GraphEdge* inNextp() const { return m_ins.nextp(); }
|
||||
V3GraphEdge* nextp(GraphWay way) const {
|
||||
return way.forward() ? outNextp() : inNextp(); }
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ public:
|
|||
bool isDelete() const { return m_deleted; }
|
||||
virtual string name() const { return m_origVertexp->name(); }
|
||||
virtual string dotColor() const { return m_origVertexp->dotColor(); }
|
||||
virtual FileLine* fileline() const { return m_origVertexp->fileline(); }
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
|
@ -63,7 +64,7 @@ public:
|
|||
class GraphAcycEdge : public V3GraphEdge {
|
||||
// userp() is always used to point to the head original graph edge
|
||||
private:
|
||||
typedef list<V3GraphEdge*> OrigEdgeList; // List of orig edges, see also GraphAcyc's decl
|
||||
typedef std::list<V3GraphEdge*> OrigEdgeList; // List of orig edges, see also GraphAcyc's decl
|
||||
V3GraphEdge* origEdgep() const {
|
||||
OrigEdgeList* oEListp = ((OrigEdgeList*)userp());
|
||||
if (!oEListp) v3fatalSrc("No original edge associated with acyc edge "<<this<<endl);
|
||||
|
|
@ -93,7 +94,7 @@ struct GraphAcycEdgeCmp {
|
|||
// CLASSES
|
||||
class GraphAcyc {
|
||||
private:
|
||||
typedef list<V3GraphEdge*> OrigEdgeList; // List of orig edges, see also GraphAcycEdge's decl
|
||||
typedef std::list<V3GraphEdge*> OrigEdgeList; // List of orig edges, see also GraphAcycEdge's decl
|
||||
// GRAPH USERS
|
||||
// origGraph
|
||||
// GraphVertex::user() GraphAycVerted* New graph node
|
||||
|
|
@ -104,7 +105,7 @@ private:
|
|||
V3Graph* m_origGraphp; // Original graph
|
||||
V3Graph m_breakGraph; // Graph with only breakable edges represented
|
||||
V3List<GraphAcycVertex*> m_work; // List of vertices with optimization work left
|
||||
vector<OrigEdgeList*> m_origEdgeDelp; // List of deletions to do when done
|
||||
std::vector<OrigEdgeList*> m_origEdgeDelp; // List of deletions to do when done
|
||||
V3EdgeFuncP m_origEdgeFuncp; // Function that says we follow this edge (in original graph)
|
||||
uint32_t m_placeStep; // Number that user() must be equal to to indicate processing
|
||||
|
||||
|
|
@ -189,7 +190,7 @@ public:
|
|||
m_placeStep = 0;
|
||||
}
|
||||
~GraphAcyc() {
|
||||
for (vector<OrigEdgeList*>::iterator it = m_origEdgeDelp.begin(); it != m_origEdgeDelp.end(); ++it) {
|
||||
for (std::vector<OrigEdgeList*>::iterator it = m_origEdgeDelp.begin(); it != m_origEdgeDelp.end(); ++it) {
|
||||
delete (*it);
|
||||
}
|
||||
m_origEdgeDelp.clear();
|
||||
|
|
@ -453,7 +454,7 @@ void GraphAcyc::place() {
|
|||
}
|
||||
UINFO(4, " Cutable edges = "<<numEdges<<endl);
|
||||
|
||||
vector<V3GraphEdge*> edges; // List of all edges to be processed
|
||||
std::vector<V3GraphEdge*> edges; // List of all edges to be processed
|
||||
edges.reserve(numEdges+1); // Make the vector properly sized right off the bat -- faster than reallocating
|
||||
for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
vertexp->user(0); // Clear in prep of next step
|
||||
|
|
@ -469,7 +470,7 @@ void GraphAcyc::place() {
|
|||
|
||||
// Process each edge in weighted order
|
||||
m_placeStep = 10;
|
||||
for (vector<V3GraphEdge*>::iterator it = edges.begin(); it!=edges.end(); ++it) {
|
||||
for (std::vector<V3GraphEdge*>::iterator it = edges.begin(); it!=edges.end(); ++it) {
|
||||
V3GraphEdge* edgep = (*it);
|
||||
placeTryEdge(edgep);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "V3Global.h"
|
||||
#include "V3GraphAlg.h"
|
||||
#include "V3GraphPathChecker.h"
|
||||
|
||||
//######################################################################
|
||||
//######################################################################
|
||||
|
|
@ -74,7 +75,7 @@ void V3Graph::deleteCutableOnlyEdges() {
|
|||
//######################################################################
|
||||
// Algorithms - weakly connected components
|
||||
|
||||
class GraphRemoveRedundant : GraphAlg {
|
||||
class GraphRemoveRedundant : GraphAlg<> {
|
||||
bool m_sumWeights; ///< Sum, rather then maximize weights
|
||||
private:
|
||||
void main() {
|
||||
|
|
@ -121,7 +122,7 @@ private:
|
|||
}
|
||||
public:
|
||||
GraphRemoveRedundant(V3Graph* graphp, V3EdgeFuncP edgeFuncp, bool sumWeights)
|
||||
: GraphAlg(graphp, edgeFuncp), m_sumWeights(sumWeights) {
|
||||
: GraphAlg<>(graphp, edgeFuncp), m_sumWeights(sumWeights) {
|
||||
main();
|
||||
}
|
||||
~GraphRemoveRedundant() {}
|
||||
|
|
@ -134,11 +135,51 @@ void V3Graph::removeRedundantEdgesSum(V3EdgeFuncP edgeFuncp) {
|
|||
GraphRemoveRedundant (this, edgeFuncp, true);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
//######################################################################
|
||||
// Algorithms - remove transitive
|
||||
|
||||
class GraphAlgRemoveTransitiveEdges : GraphAlg<> {
|
||||
public:
|
||||
explicit GraphAlgRemoveTransitiveEdges(V3Graph* graphp)
|
||||
: GraphAlg<>(graphp, NULL) {}
|
||||
void go() {
|
||||
GraphPathChecker checker(m_graphp);
|
||||
for (V3GraphVertex* vxp = m_graphp->verticesBeginp();
|
||||
vxp; vxp = vxp->verticesNextp()) {
|
||||
V3GraphEdge* deletep = NULL;
|
||||
for (V3GraphEdge* edgep = vxp->outBeginp();
|
||||
edgep; edgep = edgep->outNextp()) {
|
||||
if (deletep) {
|
||||
deletep->unlinkDelete(); deletep = NULL;
|
||||
}
|
||||
// It should be safe to modify the graph, despite using
|
||||
// the GraphPathChecker, as none of the modifications will
|
||||
// change what can be reached from what, nor should they
|
||||
// change the rank or CP of any node.
|
||||
if (checker.isTransitiveEdge(edgep)) {
|
||||
deletep = edgep;
|
||||
}
|
||||
}
|
||||
if (deletep) {
|
||||
deletep->unlinkDelete(); VL_DANGLING(deletep);
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
VL_UNCOPYABLE(GraphAlgRemoveTransitiveEdges);
|
||||
};
|
||||
|
||||
void V3Graph::removeTransitiveEdges() {
|
||||
GraphAlgRemoveTransitiveEdges(this).go();
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
//######################################################################
|
||||
// Algorithms - weakly connected components
|
||||
|
||||
class GraphAlgWeakly : GraphAlg {
|
||||
class GraphAlgWeakly : GraphAlg<> {
|
||||
private:
|
||||
void main() {
|
||||
// Initialize state
|
||||
|
|
@ -169,7 +210,7 @@ private:
|
|||
}
|
||||
public:
|
||||
GraphAlgWeakly(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
|
||||
: GraphAlg(graphp, edgeFuncp) {
|
||||
: GraphAlg<>(graphp, edgeFuncp) {
|
||||
main();
|
||||
}
|
||||
~GraphAlgWeakly() {}
|
||||
|
|
@ -183,10 +224,10 @@ void V3Graph::weaklyConnected(V3EdgeFuncP edgeFuncp) {
|
|||
//######################################################################
|
||||
// Algorithms - strongly connected components
|
||||
|
||||
class GraphAlgStrongly : GraphAlg {
|
||||
class GraphAlgStrongly : GraphAlg<> {
|
||||
private:
|
||||
uint32_t m_currentDfs; // DFS count
|
||||
vector<V3GraphVertex*> m_callTrace; // List of everything we hit processing so far
|
||||
std::vector<V3GraphVertex*> m_callTrace; // List of everything we hit processing so far
|
||||
|
||||
void main() {
|
||||
// Use Tarjan's algorithm to find the strongly connected subgraphs.
|
||||
|
|
@ -254,7 +295,7 @@ private:
|
|||
}
|
||||
public:
|
||||
GraphAlgStrongly(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
|
||||
: GraphAlg(graphp, edgeFuncp) {
|
||||
: GraphAlg<>(graphp, edgeFuncp) {
|
||||
m_currentDfs = 0;
|
||||
main();
|
||||
}
|
||||
|
|
@ -269,7 +310,7 @@ void V3Graph::stronglyConnected(V3EdgeFuncP edgeFuncp) {
|
|||
//######################################################################
|
||||
// Algorithms - ranking
|
||||
|
||||
class GraphAlgRank : GraphAlg {
|
||||
class GraphAlgRank : GraphAlg<> {
|
||||
private:
|
||||
void main() {
|
||||
// Rank each vertex, ignoring cutable edges
|
||||
|
|
@ -307,7 +348,7 @@ private:
|
|||
}
|
||||
public:
|
||||
GraphAlgRank(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
|
||||
: GraphAlg(graphp, edgeFuncp) {
|
||||
: GraphAlg<>(graphp, edgeFuncp) {
|
||||
main();
|
||||
}
|
||||
~GraphAlgRank() {}
|
||||
|
|
@ -325,9 +366,9 @@ void V3Graph::rank(V3EdgeFuncP edgeFuncp) {
|
|||
//######################################################################
|
||||
// Algorithms - ranking
|
||||
|
||||
class GraphAlgRLoops : GraphAlg {
|
||||
class GraphAlgRLoops : GraphAlg<> {
|
||||
private:
|
||||
vector<V3GraphVertex*> m_callTrace; // List of everything we hit processing so far
|
||||
std::vector<V3GraphVertex*> m_callTrace; // List of everything we hit processing so far
|
||||
bool m_done; // Exit algorithm
|
||||
|
||||
void main(V3GraphVertex* vertexp) {
|
||||
|
|
@ -365,7 +406,7 @@ private:
|
|||
}
|
||||
public:
|
||||
GraphAlgRLoops(V3Graph* graphp, V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp)
|
||||
: GraphAlg(graphp, edgeFuncp) {
|
||||
: GraphAlg<>(graphp, edgeFuncp) {
|
||||
m_done = false;
|
||||
main(vertexp);
|
||||
}
|
||||
|
|
@ -381,7 +422,7 @@ void V3Graph::reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp) {
|
|||
//######################################################################
|
||||
// Algorithms - subtrees
|
||||
|
||||
class GraphAlgSubtrees : GraphAlg {
|
||||
class GraphAlgSubtrees : GraphAlg<> {
|
||||
private:
|
||||
V3Graph* m_loopGraphp;
|
||||
|
||||
|
|
@ -412,7 +453,7 @@ private:
|
|||
public:
|
||||
GraphAlgSubtrees(V3Graph* graphp, V3Graph* loopGraphp,
|
||||
V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp)
|
||||
: GraphAlg(graphp, edgeFuncp), m_loopGraphp (loopGraphp) {
|
||||
: GraphAlg<>(graphp, edgeFuncp), m_loopGraphp (loopGraphp) {
|
||||
// Vertex::m_userp - New vertex if we have seen this vertex already
|
||||
// Edge::m_userp - New edge if we have seen this edge already
|
||||
m_graphp->userClearVertices();
|
||||
|
|
@ -460,20 +501,20 @@ struct GraphSortEdgeCmp {
|
|||
|
||||
void V3Graph::sortVertices() {
|
||||
// Sort list of vertices by rank, then fanout
|
||||
vector<V3GraphVertex*> vertices;
|
||||
std::vector<V3GraphVertex*> vertices;
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
vertices.push_back(vertexp);
|
||||
}
|
||||
std::stable_sort(vertices.begin(), vertices.end(), GraphSortVertexCmp());
|
||||
this->verticesUnlink();
|
||||
for (vector<V3GraphVertex*>::iterator it = vertices.begin(); it!=vertices.end(); ++it) {
|
||||
for (std::vector<V3GraphVertex*>::iterator it = vertices.begin(); it!=vertices.end(); ++it) {
|
||||
(*it)->verticesPushBack(this);
|
||||
}
|
||||
}
|
||||
|
||||
void V3Graph::sortEdges() {
|
||||
// Sort edges by rank then fanout of node they point to
|
||||
vector<V3GraphEdge*> edges;
|
||||
std::vector<V3GraphEdge*> edges;
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
// Make a vector
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
|
|
@ -486,7 +527,7 @@ void V3Graph::sortEdges() {
|
|||
// We know the vector contains all of the edges that were
|
||||
// there originally (didn't delete or add)
|
||||
vertexp->outUnlink();
|
||||
for (vector<V3GraphEdge*>::const_iterator it = edges.begin(); it!=edges.end(); ++it) {
|
||||
for (std::vector<V3GraphEdge*>::const_iterator it = edges.begin(); it!=edges.end(); ++it) {
|
||||
(*it)->outPushBack();
|
||||
}
|
||||
// Prep for next
|
||||
|
|
@ -507,7 +548,10 @@ void V3Graph::order() {
|
|||
|
||||
// Compute rankings again
|
||||
rank(&V3GraphEdge::followAlwaysTrue);
|
||||
orderPreRanked();
|
||||
}
|
||||
|
||||
void V3Graph::orderPreRanked() {
|
||||
// Compute fanouts
|
||||
// Vertex::m_user begin: 1 indicates processing, 2 indicates completed
|
||||
userClearVertices();
|
||||
|
|
@ -529,7 +573,7 @@ double V3Graph::orderDFSIterate(V3GraphVertex* vertexp) {
|
|||
// Compute fanouts of each node
|
||||
// If forward edge, don't double count that fanout
|
||||
if (vertexp->user() == 2) return vertexp->fanout(); // Already processed it
|
||||
if (vertexp->user() == 1) v3fatalSrc("Loop found, backward edges should be dead");
|
||||
if (vertexp->user() == 1) vertexp->v3fatalSrc("Loop found, backward edges should be dead");
|
||||
vertexp->user(1);
|
||||
double fanout = 0;
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
|
|
|
|||
|
|
@ -30,17 +30,19 @@
|
|||
// Algorithms - common class
|
||||
// For internal use, most graph algorithms use this as a base class
|
||||
|
||||
template <class T_Graph = V3Graph> // Or sometimes const V3Graph
|
||||
class GraphAlg {
|
||||
protected:
|
||||
V3Graph* m_graphp; // Graph we're operating upon
|
||||
V3EdgeFuncP m_edgeFuncp; // Function that says we follow this edge
|
||||
|
||||
inline bool followEdge(V3GraphEdge* edgep) {
|
||||
return (edgep->weight() && (m_edgeFuncp)(edgep));
|
||||
}
|
||||
GraphAlg(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
|
||||
: m_graphp(graphp), m_edgeFuncp(edgeFuncp) {}
|
||||
T_Graph* m_graphp; // Graph we're operating upon
|
||||
V3EdgeFuncP m_edgeFuncp; // Function that says we follow this edge
|
||||
// CONSTRUCTORS
|
||||
GraphAlg(T_Graph* graphp, V3EdgeFuncP edgeFuncp)
|
||||
: m_graphp(graphp), m_edgeFuncp(edgeFuncp) {}
|
||||
~GraphAlg() {}
|
||||
// METHODS
|
||||
inline bool followEdge(V3GraphEdge* edgep) {
|
||||
return (edgep->weight() && (m_edgeFuncp)(edgep));
|
||||
}
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
|
|
|||
|
|
@ -40,11 +40,11 @@ DfaVertex* DfaGraph::findStart() {
|
|||
for (V3GraphVertex* vertexp = this->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
|
||||
if (vvertexp->start()) {
|
||||
if (startp) v3fatalSrc("Multiple start points in NFA graph");
|
||||
if (startp) vertexp->v3fatalSrc("Multiple start points in NFA graph");
|
||||
startp = vvertexp;
|
||||
}
|
||||
} else {
|
||||
v3fatalSrc("Non DfaVertex in DfaGraph");
|
||||
vertexp->v3fatalSrc("Non DfaVertex in DfaGraph");
|
||||
}
|
||||
}
|
||||
if (!startp) v3fatalSrc("No start point in NFA graph");
|
||||
|
|
@ -56,15 +56,15 @@ DfaVertex* DfaGraph::findStart() {
|
|||
// Algorithms - convert NFA to a DFA
|
||||
// Uses the Subset Construction Algorithm
|
||||
|
||||
class GraphNfaToDfa : GraphAlg {
|
||||
class GraphNfaToDfa : GraphAlg<> {
|
||||
// We have two types of nodes in one graph, NFA and DFA nodes.
|
||||
// Edges from NFA to NFA come from the user, and indicate input or epsilon transitions
|
||||
// Edges from DFA to NFA indicate the NFA from which that DFA was formed.
|
||||
// Edges from DFA to DFA indicate a completed input transition
|
||||
private:
|
||||
// TYPES
|
||||
typedef deque<DfaVertex*> DfaStates;
|
||||
typedef multimap<vluint64_t,DfaVertex*> HashMap;
|
||||
typedef std::deque<DfaVertex*> DfaStates;
|
||||
typedef std::multimap<vluint64_t,DfaVertex*> HashMap;
|
||||
|
||||
// MEMBERS
|
||||
uint32_t m_step; // Processing step, so we can avoid clearUser all the time
|
||||
|
|
@ -116,7 +116,9 @@ private:
|
|||
DfaVertex* nfaStatep = static_cast<DfaVertex*>(dfaEdgep->top());
|
||||
hash ^= hashVertex(nfaStatep);
|
||||
if (debug()) {
|
||||
if (nfaStatep->user()==m_step) v3fatalSrc("DFA state points to duplicate NFA state.");
|
||||
if (nfaStatep->user()==m_step) {
|
||||
nfaStatep->v3fatalSrc("DFA state points to duplicate NFA state.");
|
||||
}
|
||||
nfaStatep->user(m_step);
|
||||
}
|
||||
}
|
||||
|
|
@ -174,7 +176,7 @@ private:
|
|||
// The order of the nodes is not deterministic; the hash thus must not depend on order of edges
|
||||
uint32_t hash = hashDfaOrigins(nfasWithInput);
|
||||
|
||||
pair <HashMap::iterator,HashMap::iterator> eqrange = m_hashMap.equal_range(hash);
|
||||
std::pair<HashMap::iterator,HashMap::iterator> eqrange = m_hashMap.equal_range(hash);
|
||||
for (HashMap::iterator it = eqrange.first; it != eqrange.second; ++it) {
|
||||
DfaVertex* testp = it->second;
|
||||
if (compareDfaOrigins(nfasWithInput, testp)) {
|
||||
|
|
@ -280,7 +282,7 @@ private:
|
|||
UINFO(9," On dfaState "<<dfaStatep<<endl);
|
||||
|
||||
// From this dfaState, what corresponding nfaStates have what inputs?
|
||||
set<int> inputs;
|
||||
std::set<int> inputs;
|
||||
// Foreach NFA state (this DFA state was formed from)
|
||||
for (V3GraphEdge* dfaEdgep = dfaStatep->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) {
|
||||
if (nfaState(dfaEdgep->top())) {
|
||||
|
|
@ -299,7 +301,7 @@ private:
|
|||
}
|
||||
|
||||
// Foreach input state (NFA inputs of this DFA state)
|
||||
for (set<int>::const_iterator inIt=inputs.begin(); inIt!=inputs.end(); ++inIt) {
|
||||
for (std::set<int>::const_iterator inIt=inputs.begin(); inIt!=inputs.end(); ++inIt) {
|
||||
DfaInput input = *inIt;
|
||||
UINFO(9," ==="<<++i<<"=======================\n");
|
||||
UINFO(9," On input "<<(void*)(input.toNodep())<<endl);
|
||||
|
|
@ -346,7 +348,7 @@ private:
|
|||
|
||||
public:
|
||||
GraphNfaToDfa(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
|
||||
: GraphAlg(graphp, edgeFuncp) {
|
||||
: GraphAlg<>(graphp, edgeFuncp) {
|
||||
m_step = 0;
|
||||
main();
|
||||
}
|
||||
|
|
@ -363,7 +365,7 @@ void DfaGraph::nfaToDfa() {
|
|||
//
|
||||
// Scan the DFA, cleaning up trailing states.
|
||||
|
||||
class DfaGraphReduce : GraphAlg {
|
||||
class DfaGraphReduce : GraphAlg<> {
|
||||
private:
|
||||
// METHODS
|
||||
static int debug() { return 0; }
|
||||
|
|
@ -402,7 +404,7 @@ private:
|
|||
m_graphp->userClearVertices();
|
||||
|
||||
DfaVertex* startp = graphp()->findStart();
|
||||
stack<V3GraphVertex*> workps; workps.push(startp);
|
||||
std::stack<V3GraphVertex*> workps; workps.push(startp);
|
||||
|
||||
// Mark all nodes connected to start
|
||||
while (!workps.empty()) {
|
||||
|
|
@ -437,14 +439,14 @@ private:
|
|||
m_graphp->userClearVertices();
|
||||
|
||||
// Find all dead vertexes
|
||||
stack<DfaVertex*> workps;
|
||||
std::stack<DfaVertex*> workps;
|
||||
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
|
||||
workps.push(vvertexp);
|
||||
vertexp->user(1);
|
||||
} else {
|
||||
// If ever remove this, need dyn cast below
|
||||
v3fatalSrc("Non DfaVertex in dfa graph");
|
||||
vertexp->v3fatalSrc("Non DfaVertex in dfa graph");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -469,7 +471,7 @@ private:
|
|||
}
|
||||
public:
|
||||
DfaGraphReduce(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
|
||||
: GraphAlg(graphp, edgeFuncp) {
|
||||
: GraphAlg<>(graphp, edgeFuncp) {
|
||||
if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_in");
|
||||
optimize_accepting_out();
|
||||
if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_acc");
|
||||
|
|
@ -505,7 +507,7 @@ void DfaGraph::dfaReduce() {
|
|||
// The user's old accept is now the new accept. This is imporant as
|
||||
// we want the virtual type of it to be intact.
|
||||
|
||||
class DfaGraphComplement : GraphAlg {
|
||||
class DfaGraphComplement : GraphAlg<> {
|
||||
private:
|
||||
// MEMBERS
|
||||
DfaVertex* m_tempNewerReject;
|
||||
|
|
@ -559,7 +561,7 @@ private:
|
|||
}
|
||||
public:
|
||||
DfaGraphComplement(V3Graph* dfagraphp, V3EdgeFuncP edgeFuncp)
|
||||
: GraphAlg(dfagraphp, edgeFuncp) {
|
||||
: GraphAlg<>(dfagraphp, edgeFuncp) {
|
||||
if (debug()>=6) m_graphp->dumpDotFilePrefixed("comp_in");
|
||||
|
||||
// Vertex::m_user begin: 1 indicates new edge, no more processing
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: DAG Path Checking
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2018 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3GraphStream.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3GraphPathChecker.h"
|
||||
|
||||
//######################################################################
|
||||
// GraphPCNode
|
||||
|
||||
struct GraphPCNode {
|
||||
// User data for each node in GraphPathChecker.
|
||||
//
|
||||
// Like the LogicMTasks's, store before and after CPs for the nodes in
|
||||
// the GraphPathChecker graph.
|
||||
//
|
||||
// Unlike the LogicMTasks's, we have no cost info for the generic graph
|
||||
// accepted by GraphPathChecker, so assume each node has unit cost.
|
||||
vluint32_t m_cp[GraphWay::NUM_WAYS];
|
||||
|
||||
// Detect if we've seen this node before in a given recursive
|
||||
// operation. We'll use this in pathExistsInternal() to avoid checking
|
||||
// the same node twice, and again in updateHalfCriticalPath() to assert
|
||||
// there are no cycles.
|
||||
vluint64_t m_seenAtGeneration;
|
||||
|
||||
// CONSTRUCTORS
|
||||
GraphPCNode() : m_seenAtGeneration(0) {
|
||||
for (int w = 0; w < GraphWay::NUM_WAYS; w++) m_cp[w] = 0;
|
||||
}
|
||||
~GraphPCNode() { }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// GraphPathChecker implementation
|
||||
|
||||
GraphPathChecker::GraphPathChecker(const V3Graph* graphp, V3EdgeFuncP edgeFuncp)
|
||||
: GraphAlg<const V3Graph>(graphp, edgeFuncp)
|
||||
, m_generation(0) {
|
||||
for (V3GraphVertex* vxp = graphp->verticesBeginp();
|
||||
vxp; vxp = vxp->verticesNextp()) {
|
||||
// Setup tracking structure for each node. If delete a vertex
|
||||
// there would be a leak, but ok as accept only const V3Graph*'s.
|
||||
vxp->userp(new GraphPCNode);
|
||||
}
|
||||
// Init critical paths in userp() for each vertex
|
||||
initHalfCriticalPaths(GraphWay::FORWARD, false);
|
||||
initHalfCriticalPaths(GraphWay::REVERSE, false);
|
||||
}
|
||||
|
||||
GraphPathChecker::~GraphPathChecker() {
|
||||
// Free every GraphPCNode
|
||||
for (V3GraphVertex* vxp = m_graphp->verticesBeginp();
|
||||
vxp; vxp = vxp->verticesNextp()) {
|
||||
GraphPCNode* nodep = static_cast<GraphPCNode*>(vxp->userp());
|
||||
delete nodep; VL_DANGLING(nodep);
|
||||
vxp->userp(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphPathChecker::initHalfCriticalPaths(GraphWay way, bool checkOnly) {
|
||||
GraphStreamUnordered order(m_graphp, way);
|
||||
GraphWay rev = way.invert();
|
||||
while (const V3GraphVertex* vertexp = order.nextp()) {
|
||||
unsigned critPathCost = 0;
|
||||
for (V3GraphEdge* edgep = vertexp->beginp(rev);
|
||||
edgep; edgep = edgep->nextp(rev)) {
|
||||
if (!m_edgeFuncp(edgep)) continue;
|
||||
|
||||
V3GraphVertex* wrelativep = edgep->furtherp(rev);
|
||||
GraphPCNode* wrelUserp = static_cast<GraphPCNode*>(wrelativep->userp());
|
||||
critPathCost = std::max(critPathCost, wrelUserp->m_cp[way] + 1);
|
||||
}
|
||||
|
||||
GraphPCNode* ourUserp = static_cast<GraphPCNode*>(vertexp->userp());
|
||||
if (checkOnly) {
|
||||
if (ourUserp->m_cp[way] != critPathCost) {
|
||||
vertexp->v3fatalSrc("Validation of critical paths failed");
|
||||
}
|
||||
} else {
|
||||
ourUserp->m_cp[way] = critPathCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GraphPathChecker::pathExistsInternal(const V3GraphVertex* ap,
|
||||
const V3GraphVertex* bp,
|
||||
unsigned* costp) {
|
||||
GraphPCNode* auserp = static_cast<GraphPCNode*>(ap->userp());
|
||||
GraphPCNode* buserp = static_cast<GraphPCNode*>(bp->userp());
|
||||
|
||||
// If have already searched this node on the current search, don't
|
||||
// recurse through it again. Since we're still searching, we must not
|
||||
// have found a path on the first go either.
|
||||
if (auserp->m_seenAtGeneration == m_generation) {
|
||||
if (costp) *costp = 0;
|
||||
return false;
|
||||
}
|
||||
auserp->m_seenAtGeneration = m_generation;
|
||||
|
||||
if (costp) *costp = 1; // count 'a' toward the search cost
|
||||
|
||||
if (ap == bp) return true;
|
||||
|
||||
// Rule out an a->b path based on their CPs
|
||||
if (auserp->m_cp[GraphWay::REVERSE] < buserp->m_cp[GraphWay::REVERSE] + 1) {
|
||||
return false;
|
||||
}
|
||||
if (buserp->m_cp[GraphWay::FORWARD] < auserp->m_cp[GraphWay::FORWARD] + 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Slow path; visit some extended family
|
||||
bool foundPath = false;
|
||||
for (V3GraphEdge* edgep = ap->outBeginp();
|
||||
edgep && !foundPath; edgep = edgep->outNextp()) {
|
||||
if (!m_edgeFuncp(edgep)) continue;
|
||||
|
||||
unsigned childCost;
|
||||
if (pathExistsInternal(edgep->top(), bp, &childCost)) {
|
||||
foundPath = true;
|
||||
}
|
||||
if (costp) *costp += childCost;
|
||||
}
|
||||
|
||||
return foundPath;
|
||||
}
|
||||
|
||||
bool GraphPathChecker::pathExistsFrom(const V3GraphVertex* fromp,
|
||||
const V3GraphVertex* top) {
|
||||
incGeneration();
|
||||
return pathExistsInternal(fromp, top);
|
||||
}
|
||||
|
||||
bool GraphPathChecker::isTransitiveEdge(const V3GraphEdge* edgep) {
|
||||
const V3GraphVertex* fromp = edgep->fromp();
|
||||
const V3GraphVertex* top = edgep->top();
|
||||
incGeneration();
|
||||
for (const V3GraphEdge* fromOutp = fromp->outBeginp();
|
||||
fromOutp; fromOutp = fromOutp->outNextp()) {
|
||||
if (fromOutp == edgep) continue;
|
||||
if (pathExistsInternal(fromOutp->top(), top)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: DAG Path Checking
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2018 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _V3GRAPHPATHCHECKER_H_
|
||||
#define _V3GRAPHPATHCHECKER_H_
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3GraphAlg.h"
|
||||
|
||||
//######################################################################
|
||||
|
||||
/// Implement pathExistsFrom() with some caching to prune the search.
|
||||
/// Far more aggressive caching/pruning is possible; for now the use cases
|
||||
/// don't rely so heavily on this class that it's necessary.
|
||||
///
|
||||
/// The graph (or at least, the subset the algorithm sees through
|
||||
/// edgeFuncp) must not change during the lifetime of the checker.
|
||||
class GraphPathChecker : GraphAlg<const V3Graph> {
|
||||
// Count "generations" which increases on operations that scan through
|
||||
// the graph. Each node is marked with the last generation that scanned
|
||||
// it, to enable asserting there are no cycles, and to avoid recursing
|
||||
// through the same node twice while searching for a path.
|
||||
vluint64_t m_generation;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
GraphPathChecker(const V3Graph* graphp,
|
||||
V3EdgeFuncP edgeFuncp = V3GraphEdge::followAlwaysTrue);
|
||||
~GraphPathChecker();
|
||||
|
||||
// METHODS
|
||||
bool pathExistsFrom(const V3GraphVertex* fromp, const V3GraphVertex* top);
|
||||
|
||||
// If have edges A->B, B->C, and A->C then A->C is considered a
|
||||
// "transitive" edge (implied by A->B and B->C) and it could be safely
|
||||
// removed. Detect such an edge.
|
||||
bool isTransitiveEdge(const V3GraphEdge* edgep);
|
||||
|
||||
private:
|
||||
bool pathExistsInternal(const V3GraphVertex* ap,
|
||||
const V3GraphVertex* bp,
|
||||
unsigned* costp = NULL);
|
||||
void initHalfCriticalPaths(GraphWay w, bool checkOnly);
|
||||
void incGeneration() { ++m_generation; }
|
||||
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
VL_UNCOPYABLE(GraphPathChecker);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Dependency graph iterator. Iterates over nodes
|
||||
// in any DAG, following dependency order.
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2018 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _V3GRAPHSTREAM_H_
|
||||
#define _V3GRAPHSTREAM_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <set>
|
||||
#include VL_INCLUDE_UNORDERED_MAP
|
||||
|
||||
#include "V3Graph.h"
|
||||
|
||||
//######################################################################
|
||||
// GraphStream
|
||||
//
|
||||
// Template 'T_Compare' is a tie-breaker for ordering nodes that the DAG
|
||||
// itself does not order. It must provide an operator() that does a logical
|
||||
// less-than on two V3GraphVertex*'s, with the same signature as
|
||||
// std::less<const V3GraphVertex*>::operator(). This does not default to
|
||||
// std::less<const V3GraphVertex*> because that is nondeterministic, and so
|
||||
// not generally safe. If you want a raw pointer compare, see
|
||||
// GraphStreamUnordered below.
|
||||
|
||||
template <class T_Compare> class GraphStream {
|
||||
private:
|
||||
// TYPES
|
||||
class VxHolder {
|
||||
public:
|
||||
// MEMBERS
|
||||
const V3GraphVertex* m_vxp; // [mtask] Vertex
|
||||
uint32_t m_pos; // Sort position
|
||||
uint32_t m_numBlockingEdges; // Number of blocking edges
|
||||
// CONSTRUCTORS
|
||||
VxHolder(const V3GraphVertex* vxp, uint32_t pos, uint32_t numBlockingEdges)
|
||||
: m_vxp(vxp)
|
||||
, m_pos(pos)
|
||||
, m_numBlockingEdges(numBlockingEdges) {}
|
||||
// METHODS
|
||||
const V3GraphVertex* vertexp() const { return m_vxp; }
|
||||
// Decrement blocking edges count, return true if the vertex is
|
||||
// newly unblocked
|
||||
bool unblock() {
|
||||
if (m_numBlockingEdges <= 0) vertexp()->v3fatalSrc("Underflow of blocking edges");
|
||||
m_numBlockingEdges--;
|
||||
return (m_numBlockingEdges == 0);
|
||||
}
|
||||
};
|
||||
|
||||
class VxHolderCmp {
|
||||
public:
|
||||
// MEMBERS
|
||||
T_Compare m_lessThan; // Sorting functor
|
||||
// CONSTRUCTORS
|
||||
explicit VxHolderCmp(const T_Compare& lessThan)
|
||||
: m_lessThan(lessThan) {}
|
||||
// METHODS
|
||||
bool operator() (const VxHolder& a, const VxHolder& b) const {
|
||||
if (m_lessThan.operator()(a.vertexp(), b.vertexp())) return true;
|
||||
if (m_lessThan.operator()(b.vertexp(), a.vertexp())) return false;
|
||||
return a.m_pos < b.m_pos;
|
||||
}
|
||||
private:
|
||||
VL_UNCOPYABLE(VxHolderCmp);
|
||||
};
|
||||
|
||||
typedef std::set<VxHolder, VxHolderCmp&> ReadyVertices;
|
||||
typedef vl_unordered_map<const V3GraphVertex*, VxHolder> WaitingVertices;
|
||||
|
||||
// MEMBERS
|
||||
VxHolderCmp m_vxHolderCmp; // Vertext comparison functor
|
||||
ReadyVertices m_readyVertices; // List of ready verticies
|
||||
WaitingVertices m_waitingVertices; // List of wiating verticies
|
||||
typename ReadyVertices::iterator m_last; // Previously returned element
|
||||
GraphWay m_way; // FORWARD or REVERSE order of traversal
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
GraphStream(const V3Graph* graphp,
|
||||
GraphWay way = GraphWay::FORWARD,
|
||||
const T_Compare& lessThan = T_Compare())
|
||||
// NOTE: Perhaps REVERSE way should also reverse the sense of the
|
||||
// lessThan function? For now the only usage of REVERSE is not
|
||||
// sensitive to its lessThan at all, so it doesn't matter.
|
||||
: m_vxHolderCmp(lessThan)
|
||||
, m_readyVertices(m_vxHolderCmp)
|
||||
, m_last(m_readyVertices.end())
|
||||
, m_way(way) {
|
||||
uint32_t pos = 0;
|
||||
for (const V3GraphVertex* vxp = graphp->verticesBeginp();
|
||||
vxp; vxp=vxp->verticesNextp()) {
|
||||
// Every vertex initially is waiting, or ready.
|
||||
if (way == GraphWay::FORWARD) {
|
||||
if (vxp->inEmpty()) {
|
||||
VxHolder newVx(vxp, pos++, 0);
|
||||
m_readyVertices.insert(newVx);
|
||||
} else {
|
||||
uint32_t depCount = 0;
|
||||
for (V3GraphEdge* depp = vxp->inBeginp();
|
||||
depp; depp = depp->inNextp()) {
|
||||
depCount++;
|
||||
}
|
||||
VxHolder newVx(vxp, pos++, depCount);
|
||||
m_waitingVertices.insert(make_pair(vxp, newVx));
|
||||
}
|
||||
} else { // REVERSE
|
||||
if (vxp->outEmpty()) {
|
||||
VxHolder newVx(vxp, pos++, 0);
|
||||
m_readyVertices.insert(newVx);
|
||||
} else {
|
||||
uint32_t depCount = 0;
|
||||
for (V3GraphEdge* depp = vxp->outBeginp();
|
||||
depp; depp = depp->outNextp()) {
|
||||
depCount++;
|
||||
}
|
||||
VxHolder newVx(vxp, pos++, depCount);
|
||||
m_waitingVertices.insert(make_pair(vxp, newVx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
~GraphStream() {}
|
||||
|
||||
// METHODS
|
||||
|
||||
// Each call to nextp() returns a unique vertex in the graph, in
|
||||
// dependency order.
|
||||
//
|
||||
// Dependencies alone don't fully specify the order. Usually a graph
|
||||
// has many "ready" vertices, any of which might return next.
|
||||
//
|
||||
// To decide among the "ready" vertices, GraphStream keeps an ordered
|
||||
// list of ready vertices, sorted first by lessThan and second by
|
||||
// original graph order.
|
||||
//
|
||||
// You might expect that nextp() would return the first item from this
|
||||
// sorted list -- but that's not what it does! What nextp() actually
|
||||
// does is to return the next item in the list, following the position
|
||||
// where the previously-returned item would have been. This maximizes
|
||||
// locality: given an appropriate lessThan, nextp() will stay on a
|
||||
// given domain (or domscope, or mtask, or whatever) for as long as
|
||||
// possible before an unmet dependency forces us to switch to another
|
||||
// one.
|
||||
//
|
||||
// Within a group of vertices that lessThan considers equivalent,
|
||||
// nextp() returns them in the original graph order (presumably also
|
||||
// good locality.) V3Order.cpp relies on this to order the logic
|
||||
// vertices within a given mtask without jumping over domains too much.
|
||||
const V3GraphVertex* nextp() {
|
||||
const V3GraphVertex* resultp = NULL;
|
||||
|
||||
typename ReadyVertices::iterator curIt;
|
||||
if (m_last == m_readyVertices.end()) {
|
||||
// First call to nextp()
|
||||
curIt = m_readyVertices.begin();
|
||||
} else {
|
||||
// Subsequent call to nextp()
|
||||
curIt = m_last;
|
||||
++curIt;
|
||||
// Remove previously-returned element
|
||||
m_readyVertices.erase(m_last);
|
||||
// Wrap curIt. Expect to wrap, and make another pass, to find
|
||||
// newly-ready elements that could have appeared ahead of the
|
||||
// m_last iterator
|
||||
if (curIt == m_readyVertices.end()) {
|
||||
curIt = m_readyVertices.begin();
|
||||
}
|
||||
}
|
||||
|
||||
if (curIt != m_readyVertices.end()) {
|
||||
resultp = curIt->vertexp();
|
||||
unblockDeps(resultp);
|
||||
} else {
|
||||
// No ready vertices; waiting should be empty too, otherwise we
|
||||
// were fed a graph with cycles (which is not supported.)
|
||||
UASSERT(m_waitingVertices.empty(), "DGS fed non-DAG");
|
||||
}
|
||||
|
||||
m_last = curIt;
|
||||
return resultp;
|
||||
}
|
||||
|
||||
private:
|
||||
void unblockDeps(const V3GraphVertex* vertexp) {
|
||||
if (m_way == GraphWay::FORWARD) {
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp();
|
||||
edgep; edgep=edgep->outNextp()) {
|
||||
V3GraphVertex* toVertexp = edgep->top();
|
||||
|
||||
typename WaitingVertices::iterator it =
|
||||
m_waitingVertices.find(toVertexp);
|
||||
if (it == m_waitingVertices.end()) {
|
||||
toVertexp->v3fatalSrc("Found edge into vertex not in waiting list.");
|
||||
}
|
||||
if (it->second.unblock()) {
|
||||
m_readyVertices.insert(it->second);
|
||||
m_waitingVertices.erase(it);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp();
|
||||
edgep; edgep=edgep->inNextp()) {
|
||||
V3GraphVertex* fromVertexp = edgep->fromp();
|
||||
|
||||
typename WaitingVertices::iterator it =
|
||||
m_waitingVertices.find(fromVertexp);
|
||||
if (it == m_waitingVertices.end()) {
|
||||
fromVertexp->v3fatalSrc("Found edge into vertex not in waiting list.");
|
||||
}
|
||||
if (it->second.unblock()) {
|
||||
m_readyVertices.insert(it->second);
|
||||
m_waitingVertices.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VL_UNCOPYABLE(GraphStream);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
// GraphStreamUnordered is GraphStream using a plain pointer compare to
|
||||
// break ties in the graph order. This WILL return nodes in
|
||||
// nondeterministic order.
|
||||
typedef GraphStream<std::less<const V3GraphVertex*> > GraphStreamUnordered;
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -52,21 +52,19 @@ private:
|
|||
|
||||
// STATE
|
||||
V3Hash m_lowerHash; // Hash of the statement we're building
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
bool m_cacheInUser4; // Use user4 to cache each V3Hash?
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void nodeHashIterate(AstNode* nodep) {
|
||||
if (!nodep->user4()) {
|
||||
if (nodep->backp()->castCFunc()
|
||||
&& !(nodep->castNodeStmt() || nodep->castCFunc())) {
|
||||
V3Hash thisHash;
|
||||
if (!m_cacheInUser4 || !nodep->user4()) {
|
||||
if (VN_IS(nodep->backp(), CFunc)
|
||||
&& !(VN_IS(nodep, NodeStmt) || VN_IS(nodep, CFunc))) {
|
||||
nodep->v3fatalSrc("Node "<<nodep->prettyTypeName()<<" in statement position but not marked stmt (node under function)");
|
||||
}
|
||||
V3Hash oldHash = m_lowerHash;
|
||||
V3Hash oldHash = m_lowerHash;
|
||||
{
|
||||
m_lowerHash = nodep->sameHash();
|
||||
if (m_lowerHash.isIllegal()) {
|
||||
|
|
@ -75,15 +73,17 @@ private:
|
|||
// For identical nodes, the type should be the same thus dtypep should be the same too
|
||||
m_lowerHash = V3Hash(m_lowerHash, V3Hash(nodep->type()<<6, V3Hash(nodep->dtypep())));
|
||||
// Now update m_lowerHash for our children's (and next children) contributions
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Store the hash value
|
||||
nodep->user4(m_lowerHash.fullValue());
|
||||
//UINFO(9, " hashnode "<<m_lowerHash<<" "<<nodep<<endl);
|
||||
}
|
||||
thisHash = m_lowerHash;
|
||||
m_lowerHash = oldHash;
|
||||
}
|
||||
// Update what will become the above node's hash
|
||||
m_lowerHash += V3Hashed::nodeHash(nodep);
|
||||
m_lowerHash += m_cacheInUser4
|
||||
? V3Hashed::nodeHash(nodep) : thisHash;
|
||||
}
|
||||
|
||||
//--------------------
|
||||
|
|
@ -98,15 +98,26 @@ private:
|
|||
public:
|
||||
// CONSTUCTORS
|
||||
explicit HashedVisitor(AstNode* nodep) {
|
||||
m_cacheInUser4 = true;
|
||||
nodeHashIterate(nodep);
|
||||
//UINFO(9," stmthash "<<hex<<V3Hashed::nodeHash(nodep)<<" "<<nodep<<endl);
|
||||
}
|
||||
explicit HashedVisitor(const AstNode* nodep) {
|
||||
m_cacheInUser4 = false;
|
||||
nodeHashIterate(const_cast<AstNode*>(nodep));
|
||||
}
|
||||
V3Hash finalHash() const { return m_lowerHash; }
|
||||
virtual ~HashedVisitor() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Hashed class functions
|
||||
|
||||
V3Hash V3Hashed::uncachedHash(const AstNode* nodep) {
|
||||
HashedVisitor visitor(nodep);
|
||||
return visitor.finalHash();
|
||||
}
|
||||
|
||||
V3Hashed::iterator V3Hashed::hashAndInsert(AstNode* nodep) {
|
||||
hash(nodep);
|
||||
return m_hashMmap.insert(make_pair(nodeHash(nodep), nodep));
|
||||
|
|
@ -144,7 +155,7 @@ void V3Hashed::dumpFile(const string& filename, bool tree) {
|
|||
const vl_unique_ptr<std::ofstream> logp (V3File::new_ofstream(filename));
|
||||
if (logp->fail()) v3fatal("Can't write "<<filename);
|
||||
|
||||
map<int,int> dist;
|
||||
std::map<int,int> dist;
|
||||
|
||||
V3Hash lasthash;
|
||||
int num_in_bucket = 0;
|
||||
|
|
@ -165,8 +176,8 @@ void V3Hashed::dumpFile(const string& filename, bool tree) {
|
|||
}
|
||||
*logp <<"\n*** STATS:\n"<<endl;
|
||||
*logp<<" #InBucket Occurrences\n";
|
||||
for (map<int,int>::iterator it=dist.begin(); it!=dist.end(); ++it) {
|
||||
*logp<<" "<<setw(9)<<it->first<<" "<<setw(12)<<it->second<<endl;
|
||||
for (std::map<int,int>::iterator it=dist.begin(); it!=dist.end(); ++it) {
|
||||
*logp<<" "<<std::setw(9)<<it->first<<" "<<std::setw(12)<<it->second<<endl;
|
||||
}
|
||||
|
||||
*logp <<"\n*** Dump:\n"<<endl;
|
||||
|
|
@ -185,7 +196,7 @@ void V3Hashed::dumpFile(const string& filename, bool tree) {
|
|||
V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep) {
|
||||
UINFO(8," findD "<<nodep<<endl);
|
||||
if (!nodep->user4p()) nodep->v3fatalSrc("Called findDuplicate on non-hashed node");
|
||||
pair <HashMmap::iterator,HashMmap::iterator> eqrange = mmap().equal_range(nodeHash(nodep));
|
||||
std::pair<HashMmap::iterator,HashMmap::iterator> eqrange = mmap().equal_range(nodeHash(nodep));
|
||||
for (HashMmap::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (nodep != node2p && sameNodes(nodep, node2p)) {
|
||||
|
|
@ -198,7 +209,7 @@ V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep) {
|
|||
V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep, V3HashedUserCheck* checkp) {
|
||||
UINFO(8," findD "<<nodep<<endl);
|
||||
if (!nodep->user4p()) nodep->v3fatalSrc("Called findDuplicate on non-hashed node");
|
||||
pair <HashMmap::iterator,HashMmap::iterator> eqrange = mmap().equal_range(nodeHash(nodep));
|
||||
std::pair<HashMmap::iterator,HashMmap::iterator> eqrange = mmap().equal_range(nodeHash(nodep));
|
||||
for (HashMmap::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (nodep != node2p && checkp->check(nodep,node2p) && sameNodes(nodep, node2p)) {
|
||||
|
|
|
|||
|
|
@ -36,11 +36,7 @@ public:
|
|||
~VHashedBase() {}
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
|
@ -58,7 +54,7 @@ class V3Hashed : public VHashedBase {
|
|||
AstUser4InUse m_inuser4;
|
||||
|
||||
// TYPES
|
||||
typedef multimap<V3Hash,AstNode*> HashMmap;
|
||||
typedef std::multimap<V3Hash,AstNode*> HashMmap;
|
||||
public:
|
||||
typedef HashMmap::iterator iterator;
|
||||
private:
|
||||
|
|
@ -87,6 +83,8 @@ public:
|
|||
void dumpFile(const string& filename, bool tree);
|
||||
void dumpFilePrefixed(const string& nameComment, bool tree=false);
|
||||
static V3Hash nodeHash(AstNode* nodep) { return V3Hash(nodep->user4p()); }
|
||||
// Hash of the nodep tree, without caching in user4.
|
||||
static V3Hash uncachedHash(const AstNode* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
123
src/V3Inline.cpp
123
src/V3Inline.cpp
|
|
@ -73,22 +73,18 @@ private:
|
|||
AstNodeModule* m_modp; // Current module
|
||||
V3Double0 m_statUnsup; // Statistic tracking
|
||||
|
||||
typedef vector<AstNodeModule*> ModVec;
|
||||
typedef std::vector<AstNodeModule*> ModVec;
|
||||
ModVec m_allMods; // All modules, in top-down order.
|
||||
|
||||
// Within the context of a given module, LocalInstanceMap maps
|
||||
// from child modules to the count of each child's local instantiations.
|
||||
typedef map<AstNodeModule*, int> LocalInstanceMap;
|
||||
typedef std::map<AstNodeModule*, int> LocalInstanceMap;
|
||||
|
||||
// We keep a LocalInstanceMap for each module in the design
|
||||
map<AstNodeModule*, LocalInstanceMap> m_instances;
|
||||
std::map<AstNodeModule*, LocalInstanceMap> m_instances;
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
void cantInline(const char* reason, bool hard) {
|
||||
if (hard) {
|
||||
if (m_modp->user2() != CIL_NOTHARD) {
|
||||
|
|
@ -110,20 +106,20 @@ private:
|
|||
m_allMods.push_back(nodep);
|
||||
m_modp->user2(CIL_MAYBE);
|
||||
m_modp->user4(0); // statement count
|
||||
if (m_modp->castIface()) {
|
||||
if (VN_IS(m_modp, Iface)) {
|
||||
// Inlining an interface means we no longer have a cell handle to resolve to.
|
||||
// If inlining moves post-scope this can perhaps be relaxed.
|
||||
cantInline("modIface",true);
|
||||
}
|
||||
if (m_modp->modPublic()) cantInline("modPublic",false);
|
||||
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstCell* nodep) {
|
||||
nodep->modp()->user3Inc(); // Inc refs
|
||||
m_instances[m_modp][nodep->modp()]++;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstPragma* nodep) {
|
||||
if (nodep->pragType() == AstPragmaType::INLINE_MODULE) {
|
||||
|
|
@ -143,7 +139,7 @@ private:
|
|||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); // Remove so don't propagate to upper cell...
|
||||
} else {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep) {
|
||||
|
|
@ -153,23 +149,23 @@ private:
|
|||
virtual void visit(AstNodeFTaskRef* nodep) {
|
||||
// Cleanup link until V3LinkDot can correct it
|
||||
if (!nodep->packagep()) nodep->taskp(NULL);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp->user4Inc(); // statement count
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
// Don't count assignments, as they'll likely flatten out
|
||||
// Still need to iterate though to nullify VarXRefs
|
||||
int oldcnt = m_modp->user4();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp->user4(oldcnt);
|
||||
}
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
// Build user2, user3, and user4 for all modules.
|
||||
// Also build m_allMods and m_instances.
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
|
||||
// Iterate through all modules in bottom-up order.
|
||||
// Make a final inlining decision for each.
|
||||
|
|
@ -201,7 +197,7 @@ private:
|
|||
|| v3Global.opt.inlineMult() < 1
|
||||
|| refs*statements < v3Global.opt.inlineMult())));
|
||||
// Packages aren't really "under" anything so they confuse this algorithm
|
||||
if (modp->castPackage()) doit = false;
|
||||
if (VN_IS(modp, Package)) doit = false;
|
||||
UINFO(4, " Inline="<<doit<<" Possible="<<allowed
|
||||
<<" Refs="<<refs<<" Stmts="<<statements<<" "<<modp<<endl);
|
||||
modp->user1(doit);
|
||||
|
|
@ -210,7 +206,7 @@ private:
|
|||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (m_modp) {
|
||||
m_modp->user4Inc(); // Inc statement count
|
||||
}
|
||||
|
|
@ -220,7 +216,7 @@ public:
|
|||
// CONSTUCTORS
|
||||
explicit InlineMarkVisitor(AstNode* nodep) {
|
||||
m_modp = NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~InlineMarkVisitor() {
|
||||
V3Stats::addStat("Optimizations, Inline unsupported", m_statUnsup);
|
||||
|
|
@ -241,11 +237,8 @@ private:
|
|||
// Output:
|
||||
// AstCell::user4p() // AstCell* of the created clone
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstCell* nodep) {
|
||||
|
|
@ -255,13 +248,13 @@ private:
|
|||
virtual void visit(AstNodeStmt* nodep) {}
|
||||
virtual void visit(AstNodeMath* nodep) {}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit InlineCollectVisitor(AstNodeModule* nodep) { // passed OLD module, not new one
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~InlineCollectVisitor() {}
|
||||
};
|
||||
|
|
@ -282,11 +275,8 @@ private:
|
|||
AstNodeModule* m_modp; // Current module
|
||||
AstCell* m_cellp; // Cell being cloned
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstCellInline* nodep) {
|
||||
|
|
@ -298,24 +288,24 @@ private:
|
|||
nodep->name(name);
|
||||
UINFO(6, " Inline "<<nodep<<endl);
|
||||
// Do CellInlines under this, but don't move them
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCell* nodep) {
|
||||
// Cell under the inline cell, need to rename to avoid conflicts
|
||||
string name = m_cellp->name() + "__DOT__" + nodep->name();
|
||||
nodep->name(name);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstModule* nodep) {
|
||||
m_renamedInterfaces.clear();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVar* nodep) {
|
||||
if (nodep->user2p()) {
|
||||
// Make an assignment, so we'll trace it properly
|
||||
// user2p is either a const or a var.
|
||||
AstConst* exprconstp = nodep->user2p()->castConst();
|
||||
AstVarRef* exprvarrefp = nodep->user2p()->castVarRef();
|
||||
AstConst* exprconstp = VN_CAST(nodep->user2p(), Const);
|
||||
AstVarRef* exprvarrefp = VN_CAST(nodep->user2p(), VarRef);
|
||||
UINFO(8,"connectto: "<<nodep->user2p()<<endl);
|
||||
if (!exprconstp && !exprvarrefp) {
|
||||
nodep->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up");
|
||||
|
|
@ -351,16 +341,16 @@ private:
|
|||
}
|
||||
}
|
||||
// Iterate won't hit AstIfaceRefDType directly as it is no longer underneath the module
|
||||
if (AstIfaceRefDType* ifacerefp = nodep->dtypep()->castIfaceRefDType()) {
|
||||
if (AstIfaceRefDType* ifacerefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) {
|
||||
m_renamedInterfaces.insert(nodep->name());
|
||||
// Each inlined cell that contain an interface variable need to copy the IfaceRefDType and point it to
|
||||
// the newly cloned interface cell.
|
||||
AstIfaceRefDType* newdp = ifacerefp->cloneTree(false)->castIfaceRefDType();
|
||||
AstIfaceRefDType* newdp = VN_CAST(ifacerefp->cloneTree(false), IfaceRefDType);
|
||||
nodep->dtypep(newdp);
|
||||
ifacerefp->addNextHere(newdp);
|
||||
// Relink to point to newly cloned cell
|
||||
if (newdp->cellp()) {
|
||||
if (AstCell* newcellp = newdp->cellp()->user4p()->castCell()) {
|
||||
if (AstCell* newcellp = VN_CAST(newdp->cellp()->user4p(), Cell)) {
|
||||
newdp->cellp(newcellp);
|
||||
newdp->cellName(newcellp->name());
|
||||
// Tag the old ifacerefp to ensure it leaves no stale reference to the inlined cell.
|
||||
|
|
@ -376,24 +366,24 @@ private:
|
|||
if (!m_cellp->isTrace()) nodep->trace(false);
|
||||
if (debug()>=9) { nodep->dumpTree(cout,"varchanged:"); }
|
||||
if (debug()>=9 && nodep->valuep()) { nodep->valuep()->dumpTree(cout,"varchangei:"); }
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep) {
|
||||
// Function under the inline cell, need to rename to avoid conflicts
|
||||
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) {
|
||||
// Typedef under the inline cell, need to rename to avoid conflicts
|
||||
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
if (nodep->varp()->user2p() // It's being converted to an alias.
|
||||
&& !nodep->varp()->user3()
|
||||
&& !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases (we just made)
|
||||
AstConst* exprconstp = nodep->varp()->user2p()->castConst();
|
||||
AstVarRef* exprvarrefp = nodep->varp()->user2p()->castVarRef();
|
||||
&& !VN_IS(nodep->backp(), AssignAlias)) { // Don't constant propagate aliases (we just made)
|
||||
AstConst* exprconstp = VN_CAST(nodep->varp()->user2p(), Const);
|
||||
AstVarRef* exprvarrefp = VN_CAST(nodep->varp()->user2p(), VarRef);
|
||||
if (exprconstp) {
|
||||
nodep->replaceWith(exprconstp->cloneTree(true));
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
|
|
@ -407,7 +397,7 @@ private:
|
|||
}
|
||||
}
|
||||
nodep->name(nodep->varp()->name());
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep) {
|
||||
// Track what scope it was originally under so V3LinkDot can resolve it
|
||||
|
|
@ -426,7 +416,7 @@ private:
|
|||
tryname = tryname.substr(0, pos);
|
||||
}
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep) {
|
||||
// Track what scope it was originally under so V3LinkDot can resolve it
|
||||
|
|
@ -436,7 +426,7 @@ private:
|
|||
nodep->dotted(m_cellp->name() + "__DOT__" + nodep->dotted());
|
||||
}
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Not needed, as V3LinkDot doesn't care about typedefs
|
||||
|
|
@ -454,15 +444,15 @@ private:
|
|||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeEntrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_cellp->name()));
|
||||
if (afterp) nodep->scopeEntrp(afterp);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverDecl* nodep) {
|
||||
// Fix path in coverage statements
|
||||
nodep->hier(VString::dot(m_cellp->prettyName(), ".", nodep->hier()));
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -470,7 +460,7 @@ public:
|
|||
InlineRelinkVisitor(AstNodeModule* cloneModp, AstNodeModule* oldModp, AstCell* cellp) {
|
||||
m_modp = oldModp;
|
||||
m_cellp = cellp;
|
||||
cloneModp->accept(*this);
|
||||
iterate(cloneModp);
|
||||
}
|
||||
virtual ~InlineRelinkVisitor() {}
|
||||
};
|
||||
|
|
@ -499,16 +489,13 @@ private:
|
|||
AstNodeModule* m_modp; // Current module
|
||||
V3Double0 m_statCells; // Statistic tracking
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
// Iterate modules backwards, in bottom-up order. Required!
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
iterateChildrenBackwards(nodep);
|
||||
}
|
||||
virtual void visit(AstIfaceRefDType* nodep) {
|
||||
if (nodep->user5()) {
|
||||
|
|
@ -520,7 +507,7 @@ private:
|
|||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCell* nodep) {
|
||||
if (nodep->modp()->user1()) { // Marked with inline request
|
||||
|
|
@ -532,7 +519,7 @@ private:
|
|||
// Better off before, as if module has multiple instantiations
|
||||
// we'll save work, and we can't call pinReconnectSimple in
|
||||
// this loop as it clone()s itself.
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
|
||||
if (!pinp->exprp()) continue;
|
||||
V3Inst::pinReconnectSimple(pinp, nodep, false);
|
||||
}
|
||||
|
|
@ -551,7 +538,7 @@ private:
|
|||
nodep->name(), nodep->modp()->origName());
|
||||
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
|
||||
// Create assignments to the pins
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
|
||||
if (!pinp->exprp()) continue;
|
||||
UINFO(6," Pin change from "<<pinp->modVarp()<<endl);
|
||||
// Make new signal; even though we'll optimize the interconnect, we
|
||||
|
|
@ -562,17 +549,17 @@ private:
|
|||
if (!pinNewVarp) pinOldVarp->v3fatalSrc("Cloning failed");
|
||||
|
||||
AstNode* connectRefp = pinp->exprp();
|
||||
if (!connectRefp->castConst() && !connectRefp->castVarRef()) {
|
||||
if (!VN_IS(connectRefp, Const) && !VN_IS(connectRefp, VarRef)) {
|
||||
pinp->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up");
|
||||
}
|
||||
if (pinNewVarp->isOutOnly() && connectRefp->castConst()) {
|
||||
if (pinNewVarp->isOutOnly() && VN_IS(connectRefp, Const)) {
|
||||
pinp->v3error("Output port is connected to a constant pin, electrical short");
|
||||
}
|
||||
|
||||
// Propagate any attributes across the interconnect
|
||||
pinNewVarp->propagateAttrFrom(pinOldVarp);
|
||||
if (connectRefp->castVarRef()) {
|
||||
connectRefp->castVarRef()->varp()->propagateAttrFrom(pinOldVarp);
|
||||
if (VN_IS(connectRefp, VarRef)) {
|
||||
VN_CAST(connectRefp, VarRef)->varp()->propagateAttrFrom(pinOldVarp);
|
||||
}
|
||||
|
||||
// One to one interconnect won't make a temporary variable.
|
||||
|
|
@ -605,14 +592,14 @@ private:
|
|||
virtual void visit(AstNodeMath* nodep) {} // Accelerate
|
||||
virtual void visit(AstNodeStmt* nodep) {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit InlineVisitor(AstNode* nodep) {
|
||||
m_modp = NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~InlineVisitor() {
|
||||
V3Stats::addStat("Optimizations, Inlined cells", m_statCells);
|
||||
|
|
@ -638,7 +625,7 @@ void V3Inline::inlineAll(AstNetlist* nodep) {
|
|||
// idea to avoid dumping the hugely exploded tree.
|
||||
AstNodeModule* nextmodp;
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
|
||||
nextmodp = modp->nextp()->castNodeModule();
|
||||
nextmodp = VN_CAST(modp->nextp(), NodeModule);
|
||||
if (modp->user1()) { // Was inlined
|
||||
modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp);
|
||||
}
|
||||
|
|
|
|||
122
src/V3Inst.cpp
122
src/V3Inst.cpp
|
|
@ -51,12 +51,8 @@ private:
|
|||
// STATE
|
||||
AstCell* m_cellp; // Current cell
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
//int m_debug; int debug() { return m_debug; }
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstCell* nodep) {
|
||||
|
|
@ -64,7 +60,7 @@ private:
|
|||
m_cellp = nodep;
|
||||
//VV***** We reset user1p() on each cell!!!
|
||||
AstNode::user1ClearTree();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_cellp = NULL;
|
||||
}
|
||||
virtual void visit(AstPin* nodep) {
|
||||
|
|
@ -73,7 +69,7 @@ private:
|
|||
UINFO(4," PIN "<<nodep<<endl);
|
||||
if (!nodep->exprp()) return; // No-connect
|
||||
if (debug()>=9) nodep->dumpTree(cout," Pin_oldb: ");
|
||||
if (nodep->modVarp()->isOutOnly() && nodep->exprp()->castConst())
|
||||
if (nodep->modVarp()->isOutOnly() && VN_IS(nodep->exprp(), Const))
|
||||
nodep->v3error("Output port is connected to a constant pin, electrical short");
|
||||
// Use user1p on the PIN to indicate we created an assign for this pin
|
||||
if (!nodep->user1SetOnce()) {
|
||||
|
|
@ -99,12 +95,12 @@ private:
|
|||
m_cellp->addNextHere(assp);
|
||||
if (debug()>=9) assp->dumpTree(cout," _new: ");
|
||||
} else if (nodep->modVarp()->isIfaceRef()
|
||||
|| (nodep->modVarp()->subDTypep()->castUnpackArrayDType()
|
||||
&& nodep->modVarp()->subDTypep()->castUnpackArrayDType()->subDTypep()->castIfaceRefDType())) {
|
||||
|| (VN_IS(nodep->modVarp()->subDTypep(), UnpackArrayDType)
|
||||
&& VN_IS(VN_CAST(nodep->modVarp()->subDTypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType))) {
|
||||
// Create an AstAssignVarScope for Vars to Cells so we can link with their scope later
|
||||
AstNode* lhsp = new AstVarXRef (exprp->fileline(), nodep->modVarp(), m_cellp->name(), false);
|
||||
AstVarRef* refp = exprp->castVarRef();
|
||||
AstVarXRef* xrefp = exprp->castVarXRef();
|
||||
const AstVarRef* refp = VN_CAST(exprp, VarRef);
|
||||
const AstVarXRef* xrefp = VN_CAST(exprp, VarXRef);
|
||||
if (!refp && !xrefp) exprp->v3fatalSrc("Interfaces: Pin is not connected to a VarRef or VarXRef");
|
||||
AstAssignVarScope* assp = new AstAssignVarScope(exprp->fileline(), lhsp, exprp);
|
||||
m_cellp->addNextHere(assp);
|
||||
|
|
@ -132,14 +128,14 @@ private:
|
|||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit InstVisitor(AstNetlist* nodep) {
|
||||
m_cellp=NULL;
|
||||
//
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~InstVisitor() {}
|
||||
};
|
||||
|
|
@ -150,28 +146,24 @@ class InstDeModVarVisitor : public AstNVisitor {
|
|||
// Expand all module variables, and save names for later reference
|
||||
private:
|
||||
// STATE
|
||||
typedef map<string,AstVar*> VarNameMap;
|
||||
typedef std::map<string,AstVar*> VarNameMap;
|
||||
VarNameMap m_modVarNameMap; // Per module, name of cloned variables
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstVar* nodep) {
|
||||
if (nodep->dtypep()->castIfaceRefDType()) {
|
||||
if (VN_IS(nodep->dtypep(), IfaceRefDType)) {
|
||||
UINFO(8," dm-1-VAR "<<nodep<<endl);
|
||||
insert(nodep);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
// Save some time
|
||||
virtual void visit(AstNodeMath*) {}
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// METHODS
|
||||
|
|
@ -198,7 +190,7 @@ public:
|
|||
void main(AstNodeModule* nodep) {
|
||||
UINFO(8," dmMODULE "<<nodep<<endl);
|
||||
m_modVarNameMap.clear();
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~InstDeModVarVisitor() {}
|
||||
};
|
||||
|
|
@ -213,26 +205,22 @@ private:
|
|||
int m_instSelNum; // Current instantiation count 0..N-1
|
||||
InstDeModVarVisitor m_deModVars; // State of variables for current cell module
|
||||
|
||||
typedef map<string,AstVar*> VarNameMap;
|
||||
typedef std::map<string,AstVar*> VarNameMap;
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstVar* nodep) {
|
||||
if (nodep->dtypep()->castUnpackArrayDType()
|
||||
&& nodep->dtypep()->castUnpackArrayDType()->subDTypep()->castIfaceRefDType()) {
|
||||
if (VN_IS(nodep->dtypep(), UnpackArrayDType)
|
||||
&& VN_IS(VN_CAST(nodep->dtypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType)) {
|
||||
UINFO(8," dv-vec-VAR "<<nodep<<endl);
|
||||
AstUnpackArrayDType* arrdtype = nodep->dtypep()->castUnpackArrayDType();
|
||||
AstUnpackArrayDType* arrdtype = VN_CAST(nodep->dtypep(), UnpackArrayDType);
|
||||
AstNode* prevp = NULL;
|
||||
for (int i = arrdtype->lsb(); i <= arrdtype->msb(); ++i) {
|
||||
string varNewName = nodep->name() + "__BRA__" + cvtToStr(i) + "__KET__";
|
||||
UINFO(8,"VAR name insert "<<varNewName<<" "<<nodep<<endl);
|
||||
if (!m_deModVars.find(varNewName)) {
|
||||
AstIfaceRefDType* ifaceRefp = arrdtype->subDTypep()->castIfaceRefDType()->cloneTree(false);
|
||||
AstIfaceRefDType* ifaceRefp = VN_CAST(arrdtype->subDTypep(), IfaceRefDType)->cloneTree(false);
|
||||
arrdtype->addNextHere(ifaceRefp);
|
||||
ifaceRefp->cellp(NULL);
|
||||
|
||||
|
|
@ -251,7 +239,7 @@ private:
|
|||
if (prevp) nodep->addNextHere(prevp);
|
||||
if (prevp && debug()==9) { prevp->dumpTree(cout, "newintf: "); cout << endl; }
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstCell* nodep) {
|
||||
|
|
@ -263,10 +251,10 @@ private:
|
|||
if (nodep->rangep()) {
|
||||
m_cellRangep = nodep->rangep();
|
||||
|
||||
AstVar* ifaceVarp = nodep->nextp()->castVar();
|
||||
AstVar* ifaceVarp = VN_CAST(nodep->nextp(), Var);
|
||||
bool isIface = ifaceVarp
|
||||
&& ifaceVarp->dtypep()->castUnpackArrayDType()
|
||||
&& ifaceVarp->dtypep()->castUnpackArrayDType()->subDTypep()->castIfaceRefDType();
|
||||
&& VN_IS(ifaceVarp->dtypep(), UnpackArrayDType)
|
||||
&& VN_IS(VN_CAST(ifaceVarp->dtypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType);
|
||||
|
||||
// Make all of the required clones
|
||||
for (int i = 0; i < m_cellRangep->elementsConst(); i++) {
|
||||
|
|
@ -287,11 +275,11 @@ private:
|
|||
// If this AstCell is actually an interface instantiation, also clone the IfaceRef
|
||||
// within the same parent module as the cell
|
||||
if (isIface) {
|
||||
AstUnpackArrayDType* arrdtype = ifaceVarp->dtypep()->castUnpackArrayDType();
|
||||
AstIfaceRefDType* origIfaceRefp = arrdtype->subDTypep()->castIfaceRefDType();
|
||||
AstUnpackArrayDType* arrdtype = VN_CAST(ifaceVarp->dtypep(), UnpackArrayDType);
|
||||
AstIfaceRefDType* origIfaceRefp = VN_CAST(arrdtype->subDTypep(), IfaceRefDType);
|
||||
origIfaceRefp->cellp(NULL);
|
||||
AstVar* varNewp = ifaceVarp->cloneTree(false);
|
||||
AstIfaceRefDType* ifaceRefp = arrdtype->subDTypep()->castIfaceRefDType()->cloneTree(false);
|
||||
AstIfaceRefDType* ifaceRefp = VN_CAST(arrdtype->subDTypep(), IfaceRefDType)->cloneTree(false);
|
||||
arrdtype->addNextHere(ifaceRefp);
|
||||
ifaceRefp->cellp(newp);
|
||||
ifaceRefp->cellName(newp->name());
|
||||
|
|
@ -302,7 +290,7 @@ private:
|
|||
if (debug()==9) { varNewp->dumpTree(cout, "newintf: "); cout << endl; }
|
||||
}
|
||||
// Fixup pins
|
||||
newp->pinsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(newp->pinsp());
|
||||
if (debug()==9) { newp->dumpTree(cout,"newcell: "); cout<<endl; }
|
||||
}
|
||||
|
||||
|
|
@ -314,7 +302,7 @@ private:
|
|||
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
} else {
|
||||
m_cellRangep = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -325,8 +313,8 @@ private:
|
|||
UINFO(4," PIN "<<nodep<<endl);
|
||||
int pinwidth = nodep->modVarp()->width();
|
||||
int expwidth = nodep->exprp()->width();
|
||||
pair<uint32_t,uint32_t> pinDim = nodep->modVarp()->dtypep()->dimensions(false);
|
||||
pair<uint32_t,uint32_t> expDim = nodep->exprp()->dtypep()->dimensions(false);
|
||||
std::pair<uint32_t,uint32_t> pinDim = nodep->modVarp()->dtypep()->dimensions(false);
|
||||
std::pair<uint32_t,uint32_t> expDim = nodep->exprp()->dtypep()->dimensions(false);
|
||||
UINFO(4," PINVAR "<<nodep->modVarp()<<endl);
|
||||
UINFO(4," EXP "<<nodep->exprp()<<endl);
|
||||
UINFO(4," pinwidth ew="<<expwidth<<" pw="<<pinwidth
|
||||
|
|
@ -348,9 +336,9 @@ private:
|
|||
}
|
||||
AstNode* exprp = nodep->exprp()->unlinkFrBack();
|
||||
bool inputPin = nodep->modVarp()->isInput();
|
||||
if (!inputPin && !exprp->castVarRef()
|
||||
&& !exprp->castConcat() // V3Const will collapse the SEL with the one we're about to make
|
||||
&& !exprp->castSel()) { // V3Const will collapse the SEL with the one we're about to make
|
||||
if (!inputPin && !VN_IS(exprp, VarRef)
|
||||
&& !VN_IS(exprp, Concat) // V3Const will collapse the SEL with the one we're about to make
|
||||
&& !VN_IS(exprp, Sel)) { // V3Const will collapse the SEL with the one we're about to make
|
||||
nodep->v3error("Unsupported: Per-bit array instantiations with output connections to non-wires.");
|
||||
// Note spec allows more complicated matches such as slices and such
|
||||
}
|
||||
|
|
@ -361,19 +349,19 @@ private:
|
|||
} else {
|
||||
nodep->v3fatalSrc("Width mismatch; V3Width should have errored out.");
|
||||
}
|
||||
} else if (AstArraySel* arrselp = nodep->exprp()->castArraySel()) {
|
||||
if (AstUnpackArrayDType* arrp = arrselp->lhsp()->dtypep()->castUnpackArrayDType()) {
|
||||
if (!arrp->subDTypep()->castIfaceRefDType())
|
||||
} else if (AstArraySel* arrselp = VN_CAST(nodep->exprp(), ArraySel)) {
|
||||
if (AstUnpackArrayDType* arrp = VN_CAST(arrselp->lhsp()->dtypep(), UnpackArrayDType)) {
|
||||
if (!VN_IS(arrp->subDTypep(), IfaceRefDType))
|
||||
return;
|
||||
|
||||
V3Const::constifyParamsEdit(arrselp->rhsp());
|
||||
AstConst* constp = arrselp->rhsp()->castConst();
|
||||
const AstConst* constp = VN_CAST(arrselp->rhsp(), Const);
|
||||
if (!constp) {
|
||||
nodep->v3error("Unsupported: Non-constant index when passing interface to module");
|
||||
return;
|
||||
}
|
||||
string index = AstNode::encodeNumber(constp->toSInt());
|
||||
AstVarRef* varrefp = arrselp->lhsp()->castVarRef();
|
||||
AstVarRef* varrefp = VN_CAST(arrselp->lhsp(), VarRef);
|
||||
AstVarXRef* newp = new AstVarXRef(nodep->fileline(), varrefp->name()+"__BRA__"+index+"__KET__", "", true);
|
||||
newp->dtypep(nodep->modVarp()->dtypep());
|
||||
newp->packagep(varrefp->packagep());
|
||||
|
|
@ -382,8 +370,8 @@ private:
|
|||
}
|
||||
} else {
|
||||
AstVar* pinVarp = nodep->modVarp();
|
||||
AstUnpackArrayDType* pinArrp = pinVarp->dtypep()->castUnpackArrayDType();
|
||||
if (!pinArrp || !pinArrp->subDTypep()->castIfaceRefDType())
|
||||
AstUnpackArrayDType* pinArrp = VN_CAST(pinVarp->dtypep(), UnpackArrayDType);
|
||||
if (!pinArrp || !VN_IS(pinArrp->subDTypep(), IfaceRefDType))
|
||||
return;
|
||||
AstNode* prevp = NULL;
|
||||
AstNode* prevPinp = NULL;
|
||||
|
|
@ -397,7 +385,7 @@ private:
|
|||
if (!pinVarp->backp()) {
|
||||
varNewp = m_deModVars.find(varNewName);
|
||||
} else {
|
||||
AstIfaceRefDType* ifaceRefp = pinArrp->subDTypep()->castIfaceRefDType();
|
||||
AstIfaceRefDType* ifaceRefp = VN_CAST(pinArrp->subDTypep(), IfaceRefDType);
|
||||
ifaceRefp->cellp(NULL);
|
||||
varNewp = pinVarp->cloneTree(false);
|
||||
varNewp->name(varNewName);
|
||||
|
|
@ -421,7 +409,7 @@ private:
|
|||
newp->modVarp(varNewp);
|
||||
newp->name(newp->name() + "__BRA__" + cvtToStr(i) + "__KET__");
|
||||
// And replace exprp with a new varxref
|
||||
AstVarRef* varrefp = newp->exprp()->castVarRef();
|
||||
const AstVarRef* varrefp = VN_CAST(newp->exprp(), VarRef);
|
||||
string newname = varrefp->name() + "__BRA__" + cvtToStr(i) + "__KET__";
|
||||
AstVarXRef* newVarXRefp = new AstVarXRef (nodep->fileline(), newname, "", true);
|
||||
newVarXRefp->varp(newp->modVarp());
|
||||
|
|
@ -448,7 +436,7 @@ private:
|
|||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
|
|
@ -456,7 +444,7 @@ public:
|
|||
m_cellRangep=NULL;
|
||||
m_instSelNum=0;
|
||||
//
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~InstDeVisitor() {}
|
||||
};
|
||||
|
|
@ -466,11 +454,7 @@ public:
|
|||
|
||||
class InstStatic {
|
||||
private:
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
InstStatic() {} // Static class
|
||||
|
||||
static AstNode* extendOrSel(FileLine* fl, AstNode* rhsp, AstNode* cmpWidthp) {
|
||||
|
|
@ -496,12 +480,12 @@ public:
|
|||
// Note this module calles cloneTree() via new AstVar
|
||||
|
||||
AstVar* pinVarp = pinp->modVarp();
|
||||
AstVarRef* connectRefp = pinp->exprp()->castVarRef();
|
||||
AstVarXRef* connectXRefp = pinp->exprp()->castVarXRef();
|
||||
AstBasicDType* pinBasicp = pinVarp->dtypep()->castBasicDType(); // Maybe NULL
|
||||
AstVarRef* connectRefp = VN_CAST(pinp->exprp(), VarRef);
|
||||
AstVarXRef* connectXRefp = VN_CAST(pinp->exprp(), VarXRef);
|
||||
AstBasicDType* pinBasicp = VN_CAST(pinVarp->dtypep(), BasicDType); // Maybe NULL
|
||||
AstBasicDType* connBasicp = NULL;
|
||||
AstAssignW* assignp = NULL;
|
||||
if (connectRefp) connBasicp = connectRefp->varp()->dtypep()->castBasicDType();
|
||||
if (connectRefp) connBasicp = VN_CAST(connectRefp->varp()->dtypep(), BasicDType);
|
||||
//
|
||||
if (!alwaysCvt
|
||||
&& connectRefp
|
||||
|
|
@ -524,7 +508,7 @@ public:
|
|||
&& !connectRefp->varp()->isSc() // Need the signal as a 'shell' to convert types
|
||||
&& connBasicp->width() == pinVarp->width()) {
|
||||
// Done. One to one interconnect won't need a temporary variable.
|
||||
} else if (!alwaysCvt && !forTristate && pinp->exprp()->castConst()) {
|
||||
} else if (!alwaysCvt && !forTristate && VN_IS(pinp->exprp(), Const)) {
|
||||
// Done. Constant.
|
||||
} else {
|
||||
// Make a new temp wire
|
||||
|
|
|
|||
|
|
@ -0,0 +1,315 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Estimate instruction count to run the logic
|
||||
// we would generate for any given AST subtree.
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2018 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <iomanip>
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3InstrCount.h"
|
||||
|
||||
/// Estimate the instruction cost for executing all logic within and below
|
||||
/// a given AST node. Note this estimates the number of instructions we'll
|
||||
/// execute, not the number we'll generate. That is, for conditionals,
|
||||
/// we'll count instructions from either the 'if' or the 'else' branch,
|
||||
/// whichever is larger. We know we won't run both.
|
||||
|
||||
class InstrCountVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstNode::user4() -> int. Path cost + 1, 0 means don't dump
|
||||
// AstNode::user5() -> bool. Processed if assertNoDups
|
||||
AstUser4InUse m_inuser4;
|
||||
|
||||
// MEMBERS
|
||||
uint32_t m_instrCount; // Running count of instructions
|
||||
const AstNode* m_startNodep; // Start node of count
|
||||
bool m_tracingCall; // Iterating into a CCall to a CFunc
|
||||
bool m_inCFunc; // Inside AstCFunc
|
||||
bool m_assertNoDups; // Check for duplicates
|
||||
std::ostream* m_osp; // Dump file
|
||||
|
||||
// TYPES
|
||||
// Little class to cleanly call startVisitBase/endVisitBase
|
||||
class VisitBase {
|
||||
private:
|
||||
// MEMBERS
|
||||
uint32_t m_savedCount;
|
||||
AstNode* m_nodep;
|
||||
InstrCountVisitor* m_visitor;
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VisitBase(InstrCountVisitor* visitor, AstNode* nodep)
|
||||
: m_nodep(nodep), m_visitor(visitor) {
|
||||
m_savedCount = m_visitor->startVisitBase(nodep);
|
||||
}
|
||||
~VisitBase() {
|
||||
m_visitor->endVisitBase(m_savedCount, m_nodep);
|
||||
}
|
||||
private:
|
||||
VL_UNCOPYABLE(VisitBase);
|
||||
};
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
InstrCountVisitor(AstNode* nodep, bool assertNoDups, std::ostream* osp)
|
||||
: m_instrCount(0),
|
||||
m_startNodep(nodep),
|
||||
m_tracingCall(false),
|
||||
m_inCFunc(false),
|
||||
m_assertNoDups(assertNoDups),
|
||||
m_osp(osp)
|
||||
{
|
||||
if (nodep) iterate(nodep);
|
||||
}
|
||||
virtual ~InstrCountVisitor() {}
|
||||
|
||||
// METHODS
|
||||
uint32_t instrCount() const { return m_instrCount; }
|
||||
|
||||
private:
|
||||
uint32_t startVisitBase(AstNode* nodep) {
|
||||
if (m_assertNoDups && !m_inCFunc) {
|
||||
// Ensure we don't count the same node twice
|
||||
//
|
||||
// We only enable this assert for the initial LogicMTask counts
|
||||
// in V3Order. We can't enable it for the 2nd pass in V3EmitC,
|
||||
// as we expect mtasks to contain common logic after V3Combine,
|
||||
// so this would fail.
|
||||
//
|
||||
// Also, we expect some collisions within calls to CFuncs
|
||||
// (which at the V3Order stage represent verilog tasks, not to
|
||||
// the CFuncs that V3Order will generate.) So don't check for
|
||||
// collisions in CFuncs.
|
||||
if (nodep->user5p()) {
|
||||
nodep->v3fatalSrc("Node originally inserted below logic vertex "
|
||||
<<static_cast<AstNode*>(nodep->user5p()));
|
||||
}
|
||||
nodep->user5p(const_cast<void*>(reinterpret_cast<const void*>(m_startNodep)));
|
||||
}
|
||||
|
||||
// Save the count, and add it back in during ~VisitBase This allows
|
||||
// debug prints to show local cost of each subtree, so we can see a
|
||||
// hierarchical view of the cost when in debug mode.
|
||||
uint32_t savedCount = m_instrCount;
|
||||
m_instrCount = nodep->instrCount();
|
||||
return savedCount;
|
||||
}
|
||||
void endVisitBase(uint32_t savedCount, AstNode* nodep) {
|
||||
UINFO(8, "cost "<<std::setw(6)<<std::left<<m_instrCount
|
||||
<<" "<<nodep<<endl);
|
||||
markCost(nodep);
|
||||
m_instrCount += savedCount;
|
||||
}
|
||||
void markCost(AstNode* nodep) {
|
||||
if (m_osp) nodep->user4(m_instrCount+1); // Else don't mark to avoid writeback
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeSel* nodep) {
|
||||
// This covers both AstArraySel and AstWordSel
|
||||
//
|
||||
// If some vector is a bazillion dwords long, and we're selecting 1
|
||||
// dword to read or write from it, our cost should be small.
|
||||
//
|
||||
// Hence, exclude the child of the AstWordSel from the computation,
|
||||
// whose cost scales with the size of the entire (maybe large) vector.
|
||||
VisitBase vb(this, nodep);
|
||||
iterateAndNextNull(nodep->bitp());
|
||||
}
|
||||
virtual void visit(AstSel* nodep) {
|
||||
// Similar to AstNodeSel above, a small select into a large vector
|
||||
// is not expensive. Count the cost of the AstSel itself (scales with
|
||||
// its width) and the cost of the lsbp() and widthp() nodes, but not
|
||||
// the fromp() node which could be disproportionately large.
|
||||
VisitBase vb(this, nodep);
|
||||
iterateAndNextNull(nodep->lsbp());
|
||||
iterateAndNextNull(nodep->widthp());
|
||||
}
|
||||
virtual void visit(AstSliceSel* nodep) {
|
||||
nodep->v3fatalSrc("AstSliceSel unhandled");
|
||||
}
|
||||
virtual void visit(AstMemberSel* nodep) {
|
||||
nodep->v3fatalSrc("AstMemberSel unhandled");
|
||||
}
|
||||
virtual void visit(AstConcat* nodep) {
|
||||
// Nop.
|
||||
//
|
||||
// Ignore concat. The problem with counting concat is that when we
|
||||
// have many things concatted together, it's not a single
|
||||
// operation, but this:
|
||||
//
|
||||
// concat(a, concat(b, concat(c, concat(d, ... ))))
|
||||
//
|
||||
// Then if we account a cost to each 'concat' that scales with its
|
||||
// width, this whole operation ends up with a cost accounting that
|
||||
// scales with N^2. Of course, the real operation isn't that
|
||||
// expensive: we won't copy each element over and over, we'll just
|
||||
// copy it once from its origin into its destination, so the actual
|
||||
// cost is linear with the size of the data. We don't need to count
|
||||
// the concat at all to reflect a linear cost; it's already there
|
||||
// in the width of the destination (which we count) and the sum of
|
||||
// the widths of the operands (ignored here).
|
||||
markCost(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
VisitBase vb(this, nodep);
|
||||
iterateAndNextNull(nodep->condp());
|
||||
uint32_t savedCount = m_instrCount;
|
||||
|
||||
UINFO(8, "ifsp:\n");
|
||||
m_instrCount = 0;
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
uint32_t ifCount = m_instrCount;
|
||||
if (nodep->branchPred() == AstBranchPred::BP_UNLIKELY) ifCount = 0;
|
||||
|
||||
UINFO(8, "elsesp:\n");
|
||||
m_instrCount = 0;
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
uint32_t elseCount = m_instrCount;
|
||||
if (nodep->branchPred() == AstBranchPred::BP_LIKELY) elseCount = 0;
|
||||
|
||||
if (ifCount >= elseCount) {
|
||||
m_instrCount = savedCount + ifCount;
|
||||
if (nodep->elsesp()) nodep->elsesp()->user4(0); // Don't dump it
|
||||
} else {
|
||||
m_instrCount = savedCount + elseCount;
|
||||
if (nodep->ifsp()) nodep->ifsp()->user4(0); // Don't dump it
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeCond* nodep) {
|
||||
// Just like if/else above, the ternary operator only evaluates
|
||||
// one of the two expressions, so only count the max.
|
||||
VisitBase vb(this, nodep);
|
||||
iterateAndNextNull(nodep->condp());
|
||||
uint32_t savedCount = m_instrCount;
|
||||
|
||||
UINFO(8, "?\n");
|
||||
m_instrCount = 0;
|
||||
iterateAndNextNull(nodep->expr1p());
|
||||
uint32_t ifCount = m_instrCount;
|
||||
|
||||
UINFO(8, ":\n");
|
||||
m_instrCount = 0;
|
||||
iterateAndNextNull(nodep->expr2p());
|
||||
uint32_t elseCount = m_instrCount;
|
||||
|
||||
if (ifCount < elseCount) {
|
||||
m_instrCount = savedCount + elseCount;
|
||||
if (nodep->expr1p()) nodep->expr1p()->user4(0); // Don't dump it
|
||||
} else {
|
||||
m_instrCount = savedCount + ifCount;
|
||||
if (nodep->expr2p()) nodep->expr2p()->user4(0); // Don't dump it
|
||||
}
|
||||
}
|
||||
virtual void visit(AstActive* nodep) {
|
||||
// You'd think that the OrderLogicVertex's would be disjoint trees
|
||||
// of stuff in the AST, but it isn't so: V3Order makes an
|
||||
// OrderLogicVertex for each ACTIVE, and then also makes an
|
||||
// OrderLogicVertex for each statement within the ACTIVE.
|
||||
//
|
||||
// To avoid double-counting costs, stop recursing and short-circuit
|
||||
// the computation for each ACTIVE.
|
||||
//
|
||||
// Our intent is that this only stops at the root node of the
|
||||
// search; there should be no actives beneath the root, as there
|
||||
// are no actives-under-actives. In any case, check that we're at
|
||||
// root:
|
||||
markCost(nodep);
|
||||
if (nodep != m_startNodep) {
|
||||
nodep->v3fatalSrc("Multiple actives, or not start node");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
VisitBase vb(this, nodep);
|
||||
iterateChildren(nodep);
|
||||
m_tracingCall = true;
|
||||
iterate(nodep->funcp());
|
||||
if (m_tracingCall) {
|
||||
nodep->v3fatalSrc("visit(AstCFunc) should have cleared m_tracingCall.");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
// Don't count a CFunc other than by tracing a call or counting it
|
||||
// from the root
|
||||
if (!m_tracingCall && (nodep != m_startNodep)) {
|
||||
nodep->v3fatalSrc("AstCFunc not under AstCCall, or not start node");
|
||||
}
|
||||
m_tracingCall = false;
|
||||
bool saved_inCFunc = m_inCFunc;
|
||||
m_inCFunc = true;
|
||||
{
|
||||
VisitBase vb(this, nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_inCFunc = saved_inCFunc;
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
VisitBase vb(this, nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
VL_UNCOPYABLE(InstrCountVisitor);
|
||||
};
|
||||
|
||||
// Iterate the graph printing the critical path marked by previous visitation
|
||||
class InstrCountDumpVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstNode::user4() -> int. Path cost, 0 means don't dump
|
||||
|
||||
// MEMBERS
|
||||
std::ostream* m_osp; // Dump file
|
||||
unsigned m_depth; // Current tree depth for printing indent
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
InstrCountDumpVisitor(AstNode* nodep, std::ostream* osp)
|
||||
: m_osp(osp), m_depth(0) {
|
||||
// No check for NULL output, so...
|
||||
if (!osp) nodep->v3fatalSrc("Don't call if not dumping");
|
||||
if (nodep) iterate(nodep);
|
||||
}
|
||||
virtual ~InstrCountDumpVisitor() {}
|
||||
|
||||
private:
|
||||
// METHODS
|
||||
string indent() { return string(m_depth, ':')+" "; }
|
||||
virtual void visit(AstNode* nodep) {
|
||||
++m_depth;
|
||||
if (unsigned costPlus1 = nodep->user4()) {
|
||||
*m_osp <<" "<<indent()
|
||||
<<"cost "<<std::setw(6)<<std::left<<(costPlus1-1)
|
||||
<<" "<<nodep<<endl;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
--m_depth;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
VL_UNCOPYABLE(InstrCountDumpVisitor);
|
||||
};
|
||||
|
||||
uint32_t V3InstrCount::count(AstNode* nodep, bool assertNoDups, std::ostream* osp) {
|
||||
InstrCountVisitor visitor(nodep, assertNoDups, osp);
|
||||
if (osp) InstrCountDumpVisitor dumper(nodep, osp);
|
||||
return visitor.instrCount();
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Estimate instruction count to run the logic
|
||||
// we would generate for any given AST subtree.
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2018 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
class AstNode;
|
||||
|
||||
class V3InstrCount {
|
||||
public:
|
||||
// Return the estimate count of instructions we'd incur while running
|
||||
// code in and under nodep.
|
||||
//
|
||||
// This is a rough estimate; we don't know what path we'll take through
|
||||
// conditionals in nodep, so we assume we take the longest path.
|
||||
//
|
||||
// If nodep is an AstActive, returns 0.
|
||||
// If nodep contains nested AstActives, raises an error.
|
||||
//
|
||||
// If assertNoDups is true, marks user5 on each AstNode scanned. Then
|
||||
// if we see the same node twice (across more than one call to count,
|
||||
// potentially) raises an error.
|
||||
// Optional osp is stream to dump critical path to.
|
||||
static uint32_t count(AstNode* nodep, bool assertNoDups, std::ostream* osp = NULL);
|
||||
};
|
||||
|
|
@ -30,14 +30,15 @@ class V3LanguageWords {
|
|||
// List of common reserved keywords
|
||||
|
||||
private:
|
||||
map<string,string> m_kwdMap; // List of keywords, and what language applies
|
||||
typedef std::map<string,string> KeywordMap;
|
||||
KeywordMap m_kwdMap; // List of keywords, and what language applies
|
||||
|
||||
void addKwd(const string& kwd, const string& why) {
|
||||
m_kwdMap.insert(make_pair(kwd,why));
|
||||
}
|
||||
public:
|
||||
string isKeyword(const string& kwd) {
|
||||
map<string,string>::iterator it = m_kwdMap.find(kwd);
|
||||
KeywordMap::iterator it = m_kwdMap.find(kwd);
|
||||
if (it == m_kwdMap.end()) return "";
|
||||
return it->second;
|
||||
}
|
||||
|
|
@ -107,8 +108,8 @@ class V3LanguageWords {
|
|||
addKwd("long", "C++ keyword");
|
||||
addKwd("map", "C++ common word");
|
||||
addKwd("module", "C++ modules TS keyword");
|
||||
addKwd("multimap", "C++ common word");
|
||||
addKwd("multiset", "C++ common word");
|
||||
addKwd("std::multimap", "C++ common word");
|
||||
addKwd("std::multiset", "C++ common word");
|
||||
addKwd("mutable", "C++ keyword");
|
||||
addKwd("namespace", "C++ keyword");
|
||||
addKwd("near", "C++ common word");
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class LifeState {
|
|||
public:
|
||||
V3Double0 m_statAssnDel; // Statistic tracking
|
||||
V3Double0 m_statAssnCon; // Statistic tracking
|
||||
vector<AstNode*> m_unlinkps;
|
||||
std::vector<AstNode*> m_unlinkps;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -61,7 +61,7 @@ public:
|
|||
~LifeState() {
|
||||
V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel);
|
||||
V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon);
|
||||
for (vector<AstNode*>::iterator it = m_unlinkps.begin(); it != m_unlinkps.end(); ++it) {
|
||||
for (std::vector<AstNode*>::iterator it = m_unlinkps.begin(); it != m_unlinkps.end(); ++it) {
|
||||
(*it)->unlinkFrBack();
|
||||
(*it)->deleteTree();
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ public:
|
|||
m_assignp = assp;
|
||||
m_constp = NULL;
|
||||
m_everSet = true;
|
||||
if (assp->rhsp()->castConst()) m_constp = assp->rhsp()->castConst();
|
||||
if (VN_IS(assp->rhsp(), Const)) m_constp = VN_CAST(assp->rhsp(), Const);
|
||||
}
|
||||
inline void complexAssign() { // A[x]=... or some complicated assignment
|
||||
m_assignp = NULL;
|
||||
|
|
@ -135,11 +135,7 @@ class LifeBlock {
|
|||
LifeBlock* m_aboveLifep; // Upper life, or NULL
|
||||
LifeState* m_statep; // Current global state
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
public:
|
||||
LifeBlock(LifeBlock* aboveLifep, LifeState* statep) {
|
||||
|
|
@ -297,11 +293,7 @@ private:
|
|||
LifeBlock* m_lifep; // Current active lifetime map for current scope
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
|
|
@ -322,42 +314,42 @@ private:
|
|||
// Similar code in V3Dead
|
||||
vluint64_t lastEdit = AstNode::editCountGbl(); // When it was last edited
|
||||
m_sideEffect = false;
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
if (lastEdit != AstNode::editCountGbl()) {
|
||||
// We changed something, try to constant propagate, but don't delete the
|
||||
// assignment as we still need nodep to remain.
|
||||
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
|
||||
}
|
||||
// Has to be direct assignment without any EXTRACTing.
|
||||
if (nodep->lhsp()->castVarRef() && !m_sideEffect && !m_noopt) {
|
||||
AstVarScope* vscp = nodep->lhsp()->castVarRef()->varScopep();
|
||||
if (VN_IS(nodep->lhsp(), VarRef) && !m_sideEffect && !m_noopt) {
|
||||
AstVarScope* vscp = VN_CAST(nodep->lhsp(), VarRef)->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope lost on variable");
|
||||
m_lifep->simpleAssign(vscp, nodep);
|
||||
} else {
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAssignDly* nodep) {
|
||||
// Don't treat as normal assign; V3Life doesn't understand time sense
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
//---- Track control flow changes
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
UINFO(4," IF "<<nodep<<endl);
|
||||
// Condition is part of PREVIOUS block
|
||||
nodep->condp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->condp());
|
||||
LifeBlock* prevLifep = m_lifep;
|
||||
LifeBlock* ifLifep = new LifeBlock (prevLifep, m_statep);
|
||||
LifeBlock* elseLifep = new LifeBlock (prevLifep, m_statep);
|
||||
{
|
||||
m_lifep = ifLifep;
|
||||
nodep->ifsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
m_lifep = prevLifep;
|
||||
}
|
||||
{
|
||||
m_lifep = elseLifep;
|
||||
nodep->elsesp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
m_lifep = prevLifep;
|
||||
}
|
||||
UINFO(4," join "<<endl);
|
||||
|
|
@ -383,14 +375,14 @@ private:
|
|||
LifeBlock* bodyLifep = new LifeBlock (prevLifep, m_statep);
|
||||
{
|
||||
m_lifep = condLifep;
|
||||
nodep->precondsp()->iterateAndNext(*this);
|
||||
nodep->condp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->precondsp());
|
||||
iterateAndNextNull(nodep->condp());
|
||||
m_lifep = prevLifep;
|
||||
}
|
||||
{
|
||||
m_lifep = bodyLifep;
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
nodep->incsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
iterateAndNextNull(nodep->incsp());
|
||||
m_lifep = prevLifep;
|
||||
}
|
||||
UINFO(4," joinfor"<<endl);
|
||||
|
|
@ -410,7 +402,7 @@ private:
|
|||
{
|
||||
m_lifep = bodyLifep;
|
||||
m_noopt = true;
|
||||
nodep->stmtsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
m_lifep = prevLifep;
|
||||
m_noopt = prev_noopt;
|
||||
}
|
||||
|
|
@ -421,11 +413,11 @@ private:
|
|||
}
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
//UINFO(4," CCALL "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Enter the function and trace it
|
||||
if (!nodep->funcp()->entryPoint()) { // else is non-inline or public function we optimize separately
|
||||
m_tracingCall = true;
|
||||
nodep->funcp()->accept(*this);
|
||||
iterate(nodep->funcp());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
|
|
@ -435,20 +427,20 @@ private:
|
|||
if (nodep->dpiImport() && !nodep->pure()) {
|
||||
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstUCFunc* nodep) {
|
||||
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCMath* nodep) {
|
||||
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstVar*) {} // Don't want varrefs under it
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -461,7 +453,7 @@ public:
|
|||
m_tracingCall = false;
|
||||
{
|
||||
m_lifep = new LifeBlock (NULL, m_statep);
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
if (m_lifep) { delete m_lifep; m_lifep=NULL; }
|
||||
}
|
||||
}
|
||||
|
|
@ -502,13 +494,13 @@ private:
|
|||
virtual void visit(AstNodeStmt*) {} // Accelerate
|
||||
virtual void visit(AstNodeMath*) {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
LifeTopVisitor(AstNetlist* nodep, LifeState* statep) {
|
||||
m_statep = statep;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~LifeTopVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@
|
|||
//
|
||||
//*************************************************************************
|
||||
// LIFE TRANSFORMATIONS:
|
||||
// Build control-flow graph with assignments and var usages
|
||||
// All modules:
|
||||
// Delete these
|
||||
// ASSIGN(Vdly, a)
|
||||
// ... {no reads or writes of a after the first write to Vdly}
|
||||
// ... {no reads of a after the first write to Vdly}
|
||||
// ASSIGNPOST(Vdly,tmp)
|
||||
// Build control-flow graph with assignments and var usages
|
||||
// All modules:
|
||||
// Delete these
|
||||
// ASSIGN(Vdly, a)
|
||||
// ... {no reads or writes of a after the first write to Vdly}
|
||||
// ... {no reads of a after the first write to Vdly}
|
||||
// ASSIGNPOST(Vdly,tmp)
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
|
|
@ -32,178 +32,337 @@
|
|||
#include "verilatedos.h"
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <memory> // for vl_unique_ptr -> auto_ptr or unique_ptr
|
||||
#include <unistd.h>
|
||||
#include VL_INCLUDE_UNORDERED_MAP
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3PartitionGraph.h"
|
||||
#include "V3GraphPathChecker.h"
|
||||
#include "V3LifePost.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//######################################################################
|
||||
// LifePost state, as a visitor of each AstNode
|
||||
|
||||
class LifePostBaseVisitor : public AstNVisitor {
|
||||
protected:
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// LifePost class functions
|
||||
|
||||
class LifePostElimVisitor : public LifePostBaseVisitor {
|
||||
class LifePostElimVisitor : public AstNVisitor {
|
||||
private:
|
||||
bool m_tracingCall; // Iterating into a CCall to a CFunc
|
||||
|
||||
// NODE STATE
|
||||
// INPUT:
|
||||
// AstVarScope::user4p() -> AstVarScope*, If set, replace this varscope with specified new one
|
||||
// AstVarScope::user4p() -> AstVarScope*, If set, replace this varscope with specified new one
|
||||
// STATE
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
|
||||
if (AstVarScope* newvscp = (AstVarScope*)vscp->user4p()) {
|
||||
UINFO(9, " Replace "<<nodep<<" to "<<newvscp<<endl);
|
||||
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), newvscp, nodep->lvalue());
|
||||
nodep->replaceWith(newrefp);
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
|
||||
if (AstVarScope* newvscp = (AstVarScope*)vscp->user4p()) {
|
||||
UINFO(9, " Replace "<<nodep<<" to "<<newvscp<<endl);
|
||||
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), newvscp, nodep->lvalue());
|
||||
nodep->replaceWith(newrefp);
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
// Only track the top scopes, not lower level functions
|
||||
if (nodep->isTop()) nodep->iterateChildren(*this);
|
||||
// Only track the top scopes, not lower level functions
|
||||
if (nodep->isTop()) iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->funcp()->entryPoint()) {
|
||||
// Enter the function and trace it
|
||||
m_tracingCall = true;
|
||||
nodep->funcp()->accept(*this);
|
||||
iterate(nodep->funcp());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstExecGraph* nodep) {
|
||||
// Can just iterate across the MTask bodies in any order. Order
|
||||
// isn't important for LifePostElimVisitor's simple substitution.
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
if (!m_tracingCall && !nodep->entryPoint()) return;
|
||||
m_tracingCall = false;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVar*) {} // Don't want varrefs under it
|
||||
virtual void visit(AstVar*) {} // Don't want varrefs under it
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit LifePostElimVisitor(AstTopScope* nodep)
|
||||
: m_tracingCall(false) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~LifePostElimVisitor() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Location within the execution graph, identified by an mtask
|
||||
// and a sequence number within the mtask:
|
||||
|
||||
struct LifeLocation {
|
||||
const ExecMTask* mtaskp;
|
||||
uint32_t sequence;
|
||||
public:
|
||||
LifeLocation() : mtaskp(NULL), sequence(0) {}
|
||||
LifeLocation(const ExecMTask* mtaskp_, uint32_t sequence_)
|
||||
: mtaskp(mtaskp_), sequence(sequence_) {}
|
||||
bool operator< (const LifeLocation& b) const {
|
||||
unsigned a_id = mtaskp ? mtaskp->id() : 0;
|
||||
unsigned b_id = b.mtaskp ? b.mtaskp->id() : 0;
|
||||
if (a_id < b_id) { return true; }
|
||||
if (b_id < a_id) { return false; }
|
||||
return sequence < b.sequence;
|
||||
}
|
||||
};
|
||||
|
||||
struct LifePostLocation {
|
||||
LifeLocation loc;
|
||||
AstAssignPost* nodep;
|
||||
LifePostLocation() : nodep(NULL) {}
|
||||
LifePostLocation(LifeLocation loc_, AstAssignPost* nodep_)
|
||||
: loc(loc_), nodep(nodep_) {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// LifePost delay elimination
|
||||
|
||||
class LifePostDlyVisitor : public LifePostBaseVisitor {
|
||||
class LifePostDlyVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Cleared on entire tree
|
||||
// AstVarScope::user() -> Sequence # of first vertex setting this var.
|
||||
// AstVarScope::user2() -> Sequence # of last consumption of this var
|
||||
// AstVarScope::user4() -> AstVarScope*: Passed to LifePostElim to substitute this var
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser4InUse m_inuser4;
|
||||
// AstVarScope::user4() -> AstVarScope*: Passed to LifePostElim to substitute this var
|
||||
AstUser4InUse m_inuser4;
|
||||
|
||||
// STATE
|
||||
uint32_t m_sequence; // Sequence number of assignments/varrefs
|
||||
V3Double0 m_statAssnDel; // Statistic tracking
|
||||
bool m_tracingCall; // Tracing a CCall to a CFunc
|
||||
uint32_t m_sequence; // Sequence number of assigns/varrefs,
|
||||
// // local to the current MTask.
|
||||
const ExecMTask* m_execMTaskp; // Current ExecMTask being processed,
|
||||
// // or NULL for serial code.
|
||||
V3Double0 m_statAssnDel; // Statistic tracking
|
||||
bool m_tracingCall; // Currently tracing a CCall to a CFunc
|
||||
|
||||
// Map each varscope to one or more locations where it's accessed.
|
||||
// These maps will not include any ASSIGNPOST accesses:
|
||||
typedef vl_unordered_map<const AstVarScope*, std::set<LifeLocation> > LocMap;
|
||||
LocMap m_reads; // VarScope read locations
|
||||
LocMap m_writes; // VarScope write locations
|
||||
|
||||
// Map each dly var to its AstAssignPost* node and the location thereof
|
||||
typedef vl_unordered_map<const AstVarScope*, LifePostLocation> PostLocMap;
|
||||
PostLocMap m_assignposts; // AssignPost dly var locations
|
||||
|
||||
const V3Graph* m_mtasksGraphp; // Mtask tracking graph
|
||||
vl_unique_ptr<GraphPathChecker> m_checker;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
bool before(const LifeLocation& a, const LifeLocation& b) {
|
||||
if (a.mtaskp == b.mtaskp) return a.sequence < b.sequence;
|
||||
return m_checker->pathExistsFrom(a.mtaskp, b.mtaskp);
|
||||
}
|
||||
bool outsideCriticalArea(LifeLocation loc,
|
||||
const std::set<LifeLocation>& dlyVarAssigns,
|
||||
LifeLocation assignPostLoc) {
|
||||
// If loc is before all of dlyVarAssigns, return true.
|
||||
// ("Before" means certain to be ordered before them at execution time.)
|
||||
// If assignPostLoc is before loc, return true.
|
||||
//
|
||||
// Otherwise, loc could fall in the "critical" area where the
|
||||
// substitution affects the result of the operation at loc, so
|
||||
// return false.
|
||||
if (!loc.mtaskp && assignPostLoc.mtaskp) {
|
||||
// This is threaded mode; 'loc' is something that happens at
|
||||
// initial/settle time, or perhaps in _eval() but outside of
|
||||
// the mtask graph.
|
||||
// In either case, it's not in the critical area.
|
||||
return true;
|
||||
}
|
||||
if (before(assignPostLoc, loc)) return true;
|
||||
for (std::set<LifeLocation>::iterator it = dlyVarAssigns.begin();
|
||||
it != dlyVarAssigns.end(); ++it) {
|
||||
if (!before(loc, *it)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void squashAssignposts() {
|
||||
for (PostLocMap::iterator it = m_assignposts.begin();
|
||||
it != m_assignposts.end(); ++it) {
|
||||
LifePostLocation* app = &it->second;
|
||||
AstVarRef* lhsp = VN_CAST(app->nodep->lhsp(), VarRef); // original var
|
||||
AstVarRef* rhsp = VN_CAST(app->nodep->rhsp(), VarRef); // dly var
|
||||
AstVarScope* dlyVarp = rhsp->varScopep();
|
||||
AstVarScope* origVarp = lhsp->varScopep();
|
||||
|
||||
// Scrunch these:
|
||||
// X1: __Vdly__q = __PVT__clk_clocks;
|
||||
// ... {no reads or writes of __PVT__q after the first write to __Vdly__q}
|
||||
// ... {no reads of __Vdly__q after the first write to __Vdly__q}
|
||||
// X2: __PVT__q = __Vdly__q;
|
||||
//
|
||||
// Into just this:
|
||||
// X1: __PVT__q = __PVT__clk_clocks;
|
||||
// X2: (nothing)
|
||||
|
||||
// More formally, with the non-sequential mtasks graph, we must
|
||||
// prove all of these before doing the scrunch:
|
||||
// 1) No reads of the dly var anywhere except for the ASSIGNPOST
|
||||
// 2) Every read of the original var either falls before each of
|
||||
// the dly var's assignments, or after the ASSIGNPOST.
|
||||
// 3) Every write of the original var either falls before each of
|
||||
// the dly var's assignments, or after the ASSIGNPOST.
|
||||
|
||||
const std::set<LifeLocation>& dlyVarAssigns = m_writes[dlyVarp];
|
||||
// Proof (1)
|
||||
const std::set<LifeLocation>& dlyVarReads = m_reads[dlyVarp];
|
||||
if (dlyVarReads.size() > 0) {
|
||||
continue; // do not scrunch, go to next LifePostLocation
|
||||
}
|
||||
|
||||
// Proof (2)
|
||||
bool canScrunch = true;
|
||||
const std::set<LifeLocation>& origVarReads = m_reads[origVarp];
|
||||
for (std::set<LifeLocation>::iterator rdLocIt = origVarReads.begin();
|
||||
rdLocIt != origVarReads.end(); ++rdLocIt) {
|
||||
if (!outsideCriticalArea(*rdLocIt, dlyVarAssigns, app->loc)) {
|
||||
canScrunch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!canScrunch) continue;
|
||||
|
||||
// Proof (3)
|
||||
const std::set<LifeLocation>& origVarWrites = m_writes[origVarp];
|
||||
for (std::set<LifeLocation>::iterator wrLocIt = origVarWrites.begin();
|
||||
wrLocIt != origVarWrites.end(); ++wrLocIt) {
|
||||
if (!outsideCriticalArea(*wrLocIt, dlyVarAssigns, app->loc)) {
|
||||
canScrunch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!canScrunch) continue;
|
||||
|
||||
// Delete and mark so LifePostElimVisitor will get it
|
||||
UINFO(4," DELETE "<<app->nodep<<endl);
|
||||
dlyVarp->user4p(origVarp);
|
||||
app->nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(app->nodep);
|
||||
++m_statAssnDel;
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) {
|
||||
AstNode::user1ClearTree(); // user1p() used on entire tree
|
||||
AstNode::user2ClearTree(); // user2p() used on entire tree
|
||||
AstNode::user4ClearTree(); // user4p() used on entire tree
|
||||
m_sequence = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
AstNode::user4ClearTree(); // user4p() used on entire tree
|
||||
|
||||
// Replace any node4p varscopes with the new scope
|
||||
LifePostElimVisitor visitor (nodep);
|
||||
// First, build maps of every location (mtask and sequence
|
||||
// within the mtask) where each varscope is read, and written.
|
||||
iterateChildren(nodep);
|
||||
|
||||
if (v3Global.opt.mtasks()) {
|
||||
if (!m_mtasksGraphp) {
|
||||
nodep->v3fatalSrc("Should have initted m_mtasksGraphp by now");
|
||||
}
|
||||
m_checker.reset(new GraphPathChecker(m_mtasksGraphp));
|
||||
} else {
|
||||
if (m_mtasksGraphp) {
|
||||
nodep->v3fatalSrc("Did not expect any m_mtasksGraphp in serial mode");
|
||||
}
|
||||
}
|
||||
|
||||
// Find all assignposts. Determine which ones can be
|
||||
// eliminated. Remove those, and mark their dly vars' user4 field
|
||||
// to indicate we should replace these dly vars with their original
|
||||
// variables.
|
||||
squashAssignposts();
|
||||
|
||||
// Replace any node4p varscopes with the new scope
|
||||
LifePostElimVisitor visitor (nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
// Consumption/generation of a variable,
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
|
||||
m_sequence++;
|
||||
if (nodep->lvalue()) {
|
||||
// First generator
|
||||
if (!vscp->user1()) vscp->user1(m_sequence);
|
||||
} else {
|
||||
// Last consumer
|
||||
vscp->user2(m_sequence);
|
||||
}
|
||||
// Consumption/generation of a variable,
|
||||
AstVarScope* vscp = nodep->varScopep();
|
||||
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
|
||||
|
||||
LifeLocation loc(m_execMTaskp, ++m_sequence);
|
||||
if (nodep->lvalue()) {
|
||||
m_writes[vscp].insert(loc);
|
||||
} else {
|
||||
m_reads[vscp].insert(loc);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAssignPre* nodep) {
|
||||
// Do not record varrefs within assign pre.
|
||||
//
|
||||
// The pre-assignment into the dly var should not count as its
|
||||
// first write; we only want to consider reads and writes that
|
||||
// would still happen if the dly var were eliminated.
|
||||
}
|
||||
virtual void visit(AstAssignPost* nodep) {
|
||||
if (AstVarRef* lhsp = nodep->lhsp()->castVarRef()) {
|
||||
if (AstVarRef* rhsp = nodep->rhsp()->castVarRef()) {
|
||||
// Scrunch these:
|
||||
// __Vdly__q = __PVT__clk_clocks;
|
||||
// ... {no reads or writes of __PVT__q after the first write to __Vdly__q}
|
||||
// ... {no reads of __Vdly__q after the first write to __Vdly__q}
|
||||
// __PVT__q = __Vdly__q;
|
||||
UINFO(9," POST "<<nodep<<endl);
|
||||
UINFO(9," lhs "<<lhsp<<endl);
|
||||
UINFO(9," rhs "<<rhsp<<endl);
|
||||
uint32_t firstWrite = rhsp->varScopep()->user1();
|
||||
uint32_t lastRead = rhsp->varScopep()->user2();
|
||||
uint32_t lastRead2 = lhsp->varScopep()->user2();
|
||||
UINFO(9," first "<<firstWrite<<" last "<<lastRead<<" "<<lastRead2<<endl);
|
||||
if (lastRead < firstWrite
|
||||
&& lastRead2 < firstWrite) {
|
||||
UINFO(4," DELETE "<<nodep<<endl);
|
||||
// Mark so LifePostElimVisitor will get it
|
||||
rhsp->varScopep()->user4p(lhsp->varScopep());
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
++m_statAssnDel;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't record ASSIGNPOST in the read/write maps, record them in a
|
||||
// separate map
|
||||
if (AstVarRef* rhsp = VN_CAST(nodep->rhsp(), VarRef)) {
|
||||
// rhsp is the dly var
|
||||
AstVarScope* dlyVarp = rhsp->varScopep();
|
||||
if (m_assignposts.find(dlyVarp) != m_assignposts.end()) {
|
||||
nodep->v3fatalSrc("LifePostLocation attempted duplicate dlyvar map addition");
|
||||
}
|
||||
LifeLocation loc(m_execMTaskp, ++m_sequence);
|
||||
m_assignposts[dlyVarp] = LifePostLocation(loc, nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
// Only track the top scopes, not lower level functions
|
||||
if (nodep->isTop()) nodep->iterateChildren(*this);
|
||||
// Only track the top scopes, not lower level functions
|
||||
if (nodep->isTop()) iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->funcp()->entryPoint()) {
|
||||
// Enter the function and trace it
|
||||
m_tracingCall = true;
|
||||
nodep->funcp()->accept(*this);
|
||||
iterate(nodep->funcp());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstExecGraph* nodep) {
|
||||
// Treat the ExecGraph like a call to each mtask body
|
||||
m_mtasksGraphp = nodep->depGraphp();
|
||||
for (V3GraphVertex* mtaskVxp = m_mtasksGraphp->verticesBeginp();
|
||||
mtaskVxp; mtaskVxp = mtaskVxp->verticesNextp()) {
|
||||
ExecMTask* mtaskp = dynamic_cast<ExecMTask*>(mtaskVxp);
|
||||
m_execMTaskp = mtaskp;
|
||||
m_sequence = 0;
|
||||
iterate(mtaskp->bodyp());
|
||||
}
|
||||
m_execMTaskp = NULL;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
if (!m_tracingCall && !nodep->entryPoint()) return;
|
||||
m_tracingCall = false;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
//-----
|
||||
virtual void visit(AstVar*) {} // Don't want varrefs under it
|
||||
virtual void visit(AstVar*) {} // Don't want varrefs under it
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit LifePostDlyVisitor(AstNetlist* nodep)
|
||||
: m_tracingCall(false) {
|
||||
nodep->accept(*this);
|
||||
: m_sequence(0)
|
||||
, m_execMTaskp(NULL)
|
||||
, m_tracingCall(false)
|
||||
, m_mtasksGraphp(NULL) {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~LifePostDlyVisitor() {
|
||||
V3Stats::addStat("Optimizations, Lifetime postassign deletions", m_statAssnDel);
|
||||
V3Stats::addStat("Optimizations, Lifetime postassign deletions", m_statAssnDel);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -214,7 +373,7 @@ void V3LifePost::lifepostAll(AstNetlist* nodep) {
|
|||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
// Mark redundant AssignPost
|
||||
{
|
||||
LifePostDlyVisitor visitor (nodep);
|
||||
LifePostDlyVisitor visitor(nodep);
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("life_post", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ public:
|
|||
virtual ~LinkCellsVertex() {}
|
||||
AstNodeModule* modp() const { return m_modp; }
|
||||
virtual string name() const { return modp()->name(); }
|
||||
virtual FileLine* fileline() const { return modp()->fileline(); }
|
||||
// Recursive modules get space for maximum recursion
|
||||
virtual uint32_t rankAdder() const { return m_modp->recursiveClone() ? (1+v3Global.opt.moduleRecursionDepth()) : 1; }
|
||||
};
|
||||
|
|
@ -113,11 +114,7 @@ private:
|
|||
V3GraphVertex* m_topVertexp; // Vertex of top module
|
||||
vl_unordered_set<string> m_declfnWarned; // Files we issued DECLFILENAME on
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// METHODS
|
||||
V3GraphVertex* vertex(AstNodeModule* nodep) {
|
||||
|
|
@ -131,7 +128,7 @@ private:
|
|||
AstNodeModule* findModuleSym(const string& modName) {
|
||||
VSymEnt* foundp = m_mods.rootp()->findIdFallback(modName);
|
||||
if (!foundp) return NULL;
|
||||
else return foundp->nodep()->castNodeModule();
|
||||
else return VN_CAST(foundp->nodep(), NodeModule);
|
||||
}
|
||||
|
||||
AstNodeModule* resolveModule(AstNode* nodep, const string& modName) {
|
||||
|
|
@ -160,7 +157,7 @@ private:
|
|||
virtual void visit(AstNetlist* nodep) {
|
||||
AstNode::user1ClearTree();
|
||||
readModNames();
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Find levels in graph
|
||||
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
|
||||
m_graph.dumpDotFilePrefixed("linkcells");
|
||||
|
|
@ -203,7 +200,7 @@ private:
|
|||
<<"' does not match "<<nodep->typeName()<<" name: "<<nodep->prettyName());
|
||||
}
|
||||
}
|
||||
if (nodep->castIface() || nodep->castPackage()) nodep->inLibrary(true); // Interfaces can't be at top, unless asked
|
||||
if (VN_IS(nodep, Iface) || VN_IS(nodep, Package)) nodep->inLibrary(true); // Interfaces can't be at top, unless asked
|
||||
bool topMatch = (v3Global.opt.topModule()==nodep->prettyName());
|
||||
if (topMatch) {
|
||||
m_topVertexp = vertex(nodep);
|
||||
|
|
@ -219,7 +216,7 @@ private:
|
|||
new V3GraphEdge(&m_graph, m_libVertexp, vertex(nodep), 1, false);
|
||||
}
|
||||
// Note AstBind also has iteration on cells
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->checkTree();
|
||||
m_modp = NULL;
|
||||
}
|
||||
|
|
@ -231,11 +228,11 @@ private:
|
|||
// but we might support modules-under-modules someday.
|
||||
AstNodeModule* modp = resolveModule(nodep, nodep->ifaceName());
|
||||
if (modp) {
|
||||
if (modp->castIface()) {
|
||||
if (VN_IS(modp, Iface)) {
|
||||
// Track module depths, so can sort list from parent down to children
|
||||
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false);
|
||||
if (!nodep->cellp()) nodep->ifacep(modp->castIface());
|
||||
} else if (modp->castNotFoundModule()) { // Will error out later
|
||||
if (!nodep->cellp()) nodep->ifacep(VN_CAST(modp, Iface));
|
||||
} else if (VN_IS(modp, NotFoundModule)) { // Will error out later
|
||||
} else {
|
||||
nodep->v3error("Non-interface used as an interface: "<<nodep->prettyName());
|
||||
}
|
||||
|
|
@ -245,7 +242,7 @@ private:
|
|||
|
||||
virtual void visit(AstPackageImport* nodep) {
|
||||
// Package Import: We need to do the package before the use of a package
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->packagep()) nodep->v3fatalSrc("Unlinked package"); // Parser should set packagep
|
||||
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(nodep->packagep()), 1, false);
|
||||
}
|
||||
|
|
@ -264,7 +261,7 @@ private:
|
|||
{
|
||||
m_modp = modp;
|
||||
modp->addStmtp(cellsp); // Important that this adds to end, as next iterate assumes does all cells
|
||||
cellsp->iterateAndNext(*this);
|
||||
iterateAndNextNull(cellsp);
|
||||
}
|
||||
m_modp = oldModp;
|
||||
}
|
||||
|
|
@ -298,7 +295,7 @@ private:
|
|||
// This lets us link the XREFs between the (uncloned) children so
|
||||
// they don't point to the same module which would
|
||||
// break parameter resolution.
|
||||
AstNodeModule* otherModp = cellmodp->user2p()->castNodeModule();
|
||||
AstNodeModule* otherModp = VN_CAST(cellmodp->user2p(), NodeModule);
|
||||
if (!otherModp) {
|
||||
otherModp = cellmodp->cloneTree(false);
|
||||
otherModp->name(otherModp->name()+"__Vrcm");
|
||||
|
|
@ -342,7 +339,7 @@ private:
|
|||
// Convert .* to list of pins
|
||||
bool pinStar = false;
|
||||
for (AstPin* nextp, *pinp = nodep->pinsp(); pinp; pinp=nextp) {
|
||||
nextp = pinp->nextp()->castPin();
|
||||
nextp = VN_CAST(pinp->nextp(), Pin);
|
||||
if (pinp->dotStar()) {
|
||||
if (pinStar) pinp->v3error("Duplicate .* in a cell");
|
||||
pinStar = true;
|
||||
|
|
@ -351,10 +348,10 @@ private:
|
|||
}
|
||||
}
|
||||
// Convert unnamed pins to pin number based assignments
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
|
||||
if (pinp->name()=="") pinp->name("__pinNumber"+cvtToStr(pinp->pinNum()));
|
||||
}
|
||||
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
|
||||
pinp->param(true);
|
||||
if (pinp->name()=="") pinp->name("__paramNumber"+cvtToStr(pinp->pinNum()));
|
||||
}
|
||||
|
|
@ -362,7 +359,7 @@ private:
|
|||
nodep->modName(nodep->modp()->name());
|
||||
// Note what pins exist
|
||||
vl_unordered_set<string> ports; // Symbol table of all connected port names
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
|
||||
if (pinp->name()=="") pinp->v3error("Connect by position is illegal in .* connected cells");
|
||||
if (!pinp->exprp()) {
|
||||
if (pinp->name().substr(0, 11) == "__pinNumber") {
|
||||
|
|
@ -378,7 +375,7 @@ private:
|
|||
// We search ports, rather than in/out declarations as they aren't resolved yet,
|
||||
// and it's easier to do it now than in V3LinkDot when we'd need to repeat steps.
|
||||
for (AstNode* portnodep = nodep->modp()->stmtsp(); portnodep; portnodep=portnodep->nextp()) {
|
||||
if (AstPort* portp = portnodep->castPort()) {
|
||||
if (const AstPort* portp = VN_CAST(portnodep, Port)) {
|
||||
if (ports.find(portp->name()) == ports.end()
|
||||
&& ports.find("__pinNumber"+cvtToStr(portp->pinNum())) == ports.end()) {
|
||||
if (pinStar) {
|
||||
|
|
@ -398,7 +395,7 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
if (nodep->modp()->castIface()) {
|
||||
if (VN_IS(nodep->modp(), Iface)) {
|
||||
// Cell really is the parent's instantiation of an interface, not a normal module
|
||||
// Make sure we have a variable to refer to this cell, so can <ifacename>.<innermember>
|
||||
// in the same way that a child does. Rename though to avoid conflict with cell.
|
||||
|
|
@ -428,7 +425,7 @@ private:
|
|||
}
|
||||
}
|
||||
if (nodep->modp()) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
UINFO(4," Link Cell done: "<<nodep<<endl);
|
||||
}
|
||||
|
|
@ -438,14 +435,14 @@ private:
|
|||
virtual void visit(AstNodeMath* nodep) {}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// METHODS
|
||||
void readModNames() {
|
||||
// Look at all modules, and store pointers to all module names
|
||||
for (AstNodeModule* nextp,* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nextp) {
|
||||
nextp = nodep->nextp()->castNodeModule();
|
||||
nextp = VN_CAST(nodep->nextp(), NodeModule);
|
||||
AstNodeModule* foundp = findModuleSym(nodep->name());
|
||||
if (foundp && foundp != nodep) {
|
||||
if (!(foundp->fileline()->warnIsOff(V3ErrorCode::MODDUP) || nodep->fileline()->warnIsOff(V3ErrorCode::MODDUP))) {
|
||||
|
|
@ -470,7 +467,7 @@ public:
|
|||
m_modp = NULL;
|
||||
m_libVertexp = NULL;
|
||||
m_topVertexp = NULL;
|
||||
rootp->accept(*this);
|
||||
iterate(rootp);
|
||||
}
|
||||
virtual ~LinkCellsVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -106,11 +106,11 @@ public:
|
|||
|
||||
private:
|
||||
// TYPES
|
||||
typedef multimap<string,VSymEnt*> NameScopeSymMap;
|
||||
typedef map<VSymEnt*,VSymEnt*> ScopeAliasMap;
|
||||
typedef set<pair<AstNodeModule*,string> > ImplicitNameSet;
|
||||
typedef vector<VSymEnt*> IfaceVarSyms;
|
||||
typedef vector<pair<AstIface*,VSymEnt*> > IfaceModSyms;
|
||||
typedef std::multimap<string,VSymEnt*> NameScopeSymMap;
|
||||
typedef std::map<VSymEnt*,VSymEnt*> ScopeAliasMap;
|
||||
typedef std::set<std::pair<AstNodeModule*,string> > ImplicitNameSet;
|
||||
typedef std::vector<VSymEnt*> IfaceVarSyms;
|
||||
typedef std::vector<std::pair<AstIface*,VSymEnt*> > IfaceModSyms;
|
||||
|
||||
static LinkDotState* s_errorThisp; // Last self, for error reporting only
|
||||
|
||||
|
|
@ -128,17 +128,14 @@ private:
|
|||
|
||||
public:
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
void dump(const string& nameComment="linkdot", bool force=false) {
|
||||
if (debug()>=6 || force) {
|
||||
string filename = v3Global.debugFilename(nameComment)+".txt";
|
||||
const vl_unique_ptr<std::ofstream> logp (V3File::new_ofstream(filename));
|
||||
if (logp->fail()) v3fatal("Can't write "<<filename);
|
||||
ostream& os = *logp;
|
||||
std::ostream& os = *logp;
|
||||
m_syms.dump(os);
|
||||
bool first = true;
|
||||
for (int samn=0; samn<SAMN__MAX; ++samn) {
|
||||
|
|
@ -191,13 +188,13 @@ public:
|
|||
|
||||
// METHODS
|
||||
static string nodeTextType(AstNode* nodep) {
|
||||
if (nodep->castVar()) return "variable";
|
||||
else if (nodep->castCell()) return "cell";
|
||||
else if (nodep->castTask()) return "task";
|
||||
else if (nodep->castFunc()) return "function";
|
||||
else if (nodep->castBegin()) return "block";
|
||||
else if (nodep->castIface()) return "interface";
|
||||
else if (nodep->castParamTypeDType()) return "parameter type";
|
||||
if (VN_IS(nodep, Var)) return "variable";
|
||||
else if (VN_IS(nodep, Cell)) return "cell";
|
||||
else if (VN_IS(nodep, Task)) return "task";
|
||||
else if (VN_IS(nodep, Func)) return "function";
|
||||
else if (VN_IS(nodep, Begin)) return "block";
|
||||
else if (VN_IS(nodep, Iface)) return "interface";
|
||||
else if (VN_IS(nodep, ParamTypeDType)) return "parameter type";
|
||||
else return nodep->prettyTypeName();
|
||||
}
|
||||
|
||||
|
|
@ -218,8 +215,8 @@ public:
|
|||
// Good.
|
||||
} else if (foundp->imported()) { // From package
|
||||
// We don't throw VARHIDDEN as if the import is later the symbol table's import wouldn't warn
|
||||
} else if (nodep->castBegin() && fnodep->castBegin()
|
||||
&& nodep->castBegin()->generate()) {
|
||||
} else if (VN_IS(nodep, Begin) && VN_IS(fnodep, Begin)
|
||||
&& VN_CAST(nodep, Begin)->generate()) {
|
||||
// Begin: ... blocks often replicate under genif/genfor, so simply suppress duplicate checks
|
||||
// See t_gen_forif.v for an example.
|
||||
} else {
|
||||
|
|
@ -373,10 +370,10 @@ public:
|
|||
}
|
||||
// Iface for a raw or arrayed iface
|
||||
static AstIfaceRefDType* ifaceRefFromArray(AstNodeDType* nodep) {
|
||||
AstIfaceRefDType* ifacerefp = nodep->castIfaceRefDType();
|
||||
AstIfaceRefDType* ifacerefp = VN_CAST(nodep, IfaceRefDType);
|
||||
if (!ifacerefp) {
|
||||
if (AstUnpackArrayDType* arrp = nodep->castUnpackArrayDType()) {
|
||||
ifacerefp = arrp->subDTypep()->castIfaceRefDType();
|
||||
if (AstUnpackArrayDType* arrp = VN_CAST(nodep, UnpackArrayDType)) {
|
||||
ifacerefp = VN_CAST(arrp->subDTypep(), IfaceRefDType);
|
||||
}
|
||||
}
|
||||
return ifacerefp;
|
||||
|
|
@ -384,7 +381,7 @@ public:
|
|||
void computeIfaceVarSyms() {
|
||||
for (IfaceVarSyms::iterator it = m_ifaceVarSyms.begin(); it != m_ifaceVarSyms.end(); ++it) {
|
||||
VSymEnt* varSymp = *it;
|
||||
AstVar* varp = varSymp ? varSymp->nodep()->castVar() : NULL;
|
||||
AstVar* varp = varSymp ? VN_CAST(varSymp->nodep(), Var) : NULL;
|
||||
UINFO(9, " insAllIface se"<<(void*)varSymp<<" "<<varp<<endl);
|
||||
AstIfaceRefDType* ifacerefp = ifaceRefFromArray(varp->subDTypep());
|
||||
if (!ifacerefp) varp->v3fatalSrc("Non-ifacerefs on list!");
|
||||
|
|
@ -406,7 +403,7 @@ public:
|
|||
VSymEnt* foundp = ifaceSymp->findIdFallback(ifacerefp->modportName());
|
||||
bool ok = false;
|
||||
if (foundp) {
|
||||
if (AstModport* modportp = foundp->nodep()->castModport()) {
|
||||
if (AstModport* modportp = VN_CAST(foundp->nodep(), Modport)) {
|
||||
UINFO(4,"Link Modport: "<<modportp<<endl);
|
||||
ifacerefp->modportp(modportp);
|
||||
ifOrPortSymp = foundp;
|
||||
|
|
@ -427,8 +424,8 @@ public:
|
|||
// Track and later insert scope aliases; an interface referenced by a child cell connecting to that interface
|
||||
// Typically lhsp=VAR w/dtype IFACEREF, rhsp=IFACE cell
|
||||
UINFO(9," insertScopeAlias se"<<(void*)lhsp<<" se"<<(void*)rhsp<<endl);
|
||||
if (rhsp->nodep()->castCell()
|
||||
&& !rhsp->nodep()->castCell()->modp()->castIface()) {
|
||||
if (VN_IS(rhsp->nodep(), Cell)
|
||||
&& !VN_IS(VN_CAST(rhsp->nodep(), Cell)->modp(), Iface)) {
|
||||
rhsp->nodep()->v3fatalSrc("Got a non-IFACE alias RHS");
|
||||
}
|
||||
m_scopeAliasMap[samn].insert(make_pair(lhsp, rhsp));
|
||||
|
|
@ -450,7 +447,7 @@ public:
|
|||
// srcp should be an interface reference pointing to the interface we want to import
|
||||
lhsp->importFromIface(symsp(), srcp);
|
||||
// Allow access to objects not permissible to be listed in a modport
|
||||
if (srcp->nodep()->castModport()) {
|
||||
if (VN_IS(srcp->nodep(), Modport)) {
|
||||
lhsp->importFromIface(symsp(), srcp->parentp(), true);
|
||||
}
|
||||
}
|
||||
|
|
@ -502,8 +499,8 @@ public:
|
|||
// then look up (inst name or modname)
|
||||
if (firstId) {
|
||||
// Check this module - subcellnames
|
||||
AstCell* cellp = lookupSymp ? lookupSymp->nodep()->castCell() : NULL; // Replicated below
|
||||
AstCellInline* inlinep = lookupSymp ? lookupSymp->nodep()->castCellInline() : NULL; // Replicated below
|
||||
AstCell* cellp = lookupSymp ? VN_CAST(lookupSymp->nodep(), Cell) : NULL; // Replicated below
|
||||
AstCellInline* inlinep = lookupSymp ? VN_CAST(lookupSymp->nodep(), CellInline) : NULL; // Replicated below
|
||||
if (VSymEnt* findSymp = findWithAltFallback(lookupSymp, ident, altIdent)) {
|
||||
lookupSymp = findSymp;
|
||||
}
|
||||
|
|
@ -515,8 +512,8 @@ public:
|
|||
bool crossedCell = false; // Crossed a cell boundary
|
||||
while (lookupSymp) {
|
||||
lookupSymp = lookupSymp->parentp();
|
||||
cellp = lookupSymp ? lookupSymp->nodep()->castCell() : NULL; // Replicated above
|
||||
inlinep = lookupSymp ? lookupSymp->nodep()->castCellInline() : NULL; // Replicated above
|
||||
cellp = lookupSymp ? VN_CAST(lookupSymp->nodep(), Cell) : NULL; // Replicated above
|
||||
inlinep = lookupSymp ? VN_CAST(lookupSymp->nodep(), CellInline) : NULL; // Replicated above
|
||||
if (lookupSymp) {
|
||||
UINFO(9,"\t\tUp to "<<lookupSymp<<endl);
|
||||
if (cellp || inlinep) {
|
||||
|
|
@ -528,7 +525,7 @@ public:
|
|||
}
|
||||
else if (VSymEnt* findSymp = findWithAltFallback(lookupSymp, ident, altIdent)) {
|
||||
lookupSymp = findSymp;
|
||||
if (crossedCell && lookupSymp->nodep()->castVar()) {
|
||||
if (crossedCell && VN_IS(lookupSymp->nodep(), Var)) {
|
||||
UINFO(9,"\t\tNot found but matches var name in parent "<<lookupSymp<<endl);
|
||||
return NULL; // Not found (but happens to be var name in parent)
|
||||
}
|
||||
|
|
@ -647,13 +644,13 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
virtual void visit(AstNetlist* nodep) {
|
||||
// Process $unit or other packages
|
||||
// Not needed - dotted references not allowed from inside packages
|
||||
//for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {
|
||||
// if (nodep->castPackage()) {}}
|
||||
//for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) {
|
||||
// if (VN_IS(nodep, Package)) {}}
|
||||
|
||||
m_statep->insertDUnit(nodep);
|
||||
|
||||
// First back iterate, to find all packages. Backward as must do base packages before using packages
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
iterateChildrenBackwards(nodep);
|
||||
|
||||
// The first module in the list is always the top module (sorted before this is called).
|
||||
// This may not be the module with isTop() set, as early in the steps,
|
||||
|
|
@ -666,7 +663,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
m_scope = "TOP";
|
||||
m_curSymp = m_modSymp = m_statep->insertTopCell(topmodp, m_scope);
|
||||
{
|
||||
topmodp->accept(*this);
|
||||
iterate(topmodp);
|
||||
}
|
||||
m_scope = "";
|
||||
m_curSymp = m_modSymp = NULL;
|
||||
|
|
@ -679,7 +676,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
UINFO(8," "<<nodep<<endl);
|
||||
// m_curSymp/m_modSymp maybe NULL for packages and non-top modules
|
||||
// Packages will be under top after the initial phases, but until then need separate handling
|
||||
bool standalonePkg = !m_modSymp && (m_statep->forPrearray() && nodep->castPackage());
|
||||
bool standalonePkg = !m_modSymp && (m_statep->forPrearray() && VN_IS(nodep, Package));
|
||||
bool doit = (m_modSymp || standalonePkg);
|
||||
string oldscope = m_scope;
|
||||
VSymEnt* oldModSymp = m_modSymp;
|
||||
|
|
@ -694,7 +691,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
UINFO(4," Link Module: "<<nodep<<endl);
|
||||
if (nodep->dead()) nodep->v3fatalSrc("Module in cell tree mislabeled as dead?");
|
||||
VSymEnt* upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp();
|
||||
m_packagep = nodep->castPackage();
|
||||
m_packagep = VN_CAST(nodep, Package);
|
||||
if (standalonePkg) {
|
||||
if (m_packagep->isDollarUnit()) {
|
||||
m_curSymp = m_modSymp = m_statep->dunitEntp();
|
||||
|
|
@ -712,11 +709,11 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
|
||||
// Iterate
|
||||
nodep->user2(true);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->user2(false);
|
||||
nodep->user4(true);
|
||||
// Interfaces need another pass when signals are resolved
|
||||
if (AstIface* ifacep = nodep->castIface()) {
|
||||
if (AstIface* ifacep = VN_CAST(nodep, Iface)) {
|
||||
m_statep->insertIfaceModSym(ifacep, m_curSymp);
|
||||
}
|
||||
} else { //!doit
|
||||
|
|
@ -741,7 +738,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
UINFO(5," CELL under "<<m_scope<<" is "<<nodep<<endl);
|
||||
// Process XREFs/etc inside pins
|
||||
if (nodep->recursive() && m_inRecursion) return;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Recurse in, preserving state
|
||||
string oldscope = m_scope;
|
||||
AstBegin* oldbeginp = m_beginp;
|
||||
|
|
@ -769,7 +766,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
m_beginp = NULL;
|
||||
m_inRecursion = nodep->recursive();
|
||||
// We don't report NotFoundModule, as may be a unused module in a generate
|
||||
if (nodep->modp()) nodep->modp()->accept(*this);
|
||||
if (nodep->modp()) iterate(nodep->modp());
|
||||
}
|
||||
m_scope = oldscope;
|
||||
m_beginp = oldbeginp;
|
||||
|
|
@ -800,7 +797,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
}
|
||||
virtual void visit(AstDefParam* nodep) {
|
||||
nodep->user1p(m_curSymp);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstGenerate* nodep) {
|
||||
// Begin: ... blocks often replicate under genif/genfor, so simply suppress duplicate checks
|
||||
|
|
@ -808,7 +805,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
bool lastInGen = m_inGenerate;
|
||||
{
|
||||
m_inGenerate = true;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_inGenerate = lastInGen;
|
||||
}
|
||||
|
|
@ -832,7 +829,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
// places such as tasks, where "task ...; begin ... end"
|
||||
// are common.
|
||||
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
|
||||
if (stmtp->castVar()) {
|
||||
if (VN_IS(stmtp, Var)) {
|
||||
++m_modBeginNum;
|
||||
nodep->name("unnamedblk"+cvtToStr(m_modBeginNum));
|
||||
break;
|
||||
|
|
@ -848,7 +845,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_packagep);
|
||||
m_curSymp->fallbackp(oldCurSymp);
|
||||
// Iterate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_curSymp = oldCurSymp;
|
||||
m_beginp = oldbegin;
|
||||
|
|
@ -868,8 +865,8 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
// This should probably be done in the Parser instead, as then we could
|
||||
// just attact normal signal attributes to it.
|
||||
if (nodep->fvarp()
|
||||
&& !nodep->fvarp()->castVar()) {
|
||||
AstNodeDType* dtypep = nodep->fvarp()->castNodeDType();
|
||||
&& !VN_IS(nodep->fvarp(), Var)) {
|
||||
AstNodeDType* dtypep = VN_CAST(nodep->fvarp(), NodeDType);
|
||||
// If unspecified, function returns one bit; however when we support NEW() it could
|
||||
// also return the class reference.
|
||||
if (dtypep) dtypep->unlinkFrBack();
|
||||
|
|
@ -884,7 +881,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
m_statep->insertSym(m_curSymp, newvarp->name(), newvarp, NULL/*packagep*/);
|
||||
}
|
||||
m_ftaskp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_ftaskp = NULL;
|
||||
}
|
||||
m_curSymp = oldCurSymp;
|
||||
|
|
@ -892,12 +889,12 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
virtual void visit(AstVar* nodep) {
|
||||
// Var: Remember its name for later resolution
|
||||
if (!m_curSymp || !m_modSymp) nodep->v3fatalSrc("Var not under module?");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!m_statep->forScopeCreation()) {
|
||||
// Find under either a task or the module's vars
|
||||
VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name());
|
||||
if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) foundp = m_modSymp; // Conflicts with modname?
|
||||
AstVar* findvarp = foundp ? foundp->nodep()->castVar() : NULL;
|
||||
AstVar* findvarp = foundp ? VN_CAST(foundp->nodep(), Var) : NULL;
|
||||
bool ins=false;
|
||||
if (!foundp) {
|
||||
ins=true;
|
||||
|
|
@ -913,7 +910,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
|| (findvarp->isSignal() && nodep->isIO())) {
|
||||
findvarp->combineType(nodep);
|
||||
nodep->fileline()->modifyStateInherit(nodep->fileline());
|
||||
AstBasicDType* bdtypep = findvarp->childDTypep()->castBasicDType();
|
||||
AstBasicDType* bdtypep = VN_CAST(findvarp->childDTypep(), BasicDType);
|
||||
if (bdtypep && bdtypep->implicit()) {
|
||||
// Then have "input foo" and "real foo" so the dtype comes from the other side.
|
||||
AstNodeDType* newdtypep = nodep->subDTypep();
|
||||
|
|
@ -976,12 +973,12 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
virtual void visit(AstTypedef* nodep) {
|
||||
// Remember its name for later resolution
|
||||
if (!m_curSymp) nodep->v3fatalSrc("Typedef not under module?");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
|
||||
}
|
||||
virtual void visit(AstParamTypeDType* nodep) {
|
||||
if (!m_curSymp) nodep->v3fatalSrc("Parameter type not under module?");
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
|
|
@ -990,11 +987,11 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
}
|
||||
virtual void visit(AstEnumItem* nodep) {
|
||||
// EnumItem: Remember its name for later resolution
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Find under either a task or the module's vars
|
||||
VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name());
|
||||
if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) foundp = m_modSymp; // Conflicts with modname?
|
||||
AstEnumItem* findvarp = foundp ? foundp->nodep()->castEnumItem() : NULL;
|
||||
AstEnumItem* findvarp = foundp ? VN_CAST(foundp->nodep(), EnumItem) : NULL;
|
||||
bool ins=false;
|
||||
if (!foundp) {
|
||||
ins=true;
|
||||
|
|
@ -1052,7 +1049,7 @@ class LinkDotFindVisitor : public AstNVisitor {
|
|||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -1070,7 +1067,7 @@ public:
|
|||
m_beginNum = 0;
|
||||
m_modBeginNum = 0;
|
||||
//
|
||||
rootp->accept(*this);
|
||||
iterate(rootp);
|
||||
}
|
||||
virtual ~LinkDotFindVisitor() {}
|
||||
};
|
||||
|
|
@ -1094,10 +1091,10 @@ private:
|
|||
void pinImplicitExprRecurse(AstNode* nodep) {
|
||||
// Under a pin, Check interconnect expression for a pin reference or a concat.
|
||||
// Create implicit variable as needed
|
||||
if (nodep->castDot()) { // Not creating a simple implied type,
|
||||
if (VN_IS(nodep, Dot)) { // Not creating a simple implied type,
|
||||
// and implying something else would just confuse later errors
|
||||
}
|
||||
else if (nodep->castVarRef() || nodep->castParseRef()) {
|
||||
else if (VN_IS(nodep, VarRef) || VN_IS(nodep, ParseRef)) {
|
||||
// To prevent user errors, we should only do single bit
|
||||
// implicit vars, however some netlists (MIPS) expect single
|
||||
// bit implicit wires to get created with range 0:0 etc.
|
||||
|
|
@ -1127,7 +1124,7 @@ private:
|
|||
nodep->dead(true);
|
||||
} else {
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -1140,10 +1137,10 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstDefParam* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
nodep->v3warn(DEFPARAM,"Suggest replace defparam with Verilog 2001 #(."<<nodep->prettyName()<<"(...etc...))");
|
||||
VSymEnt* foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->path());
|
||||
AstCell* cellp = foundp ? foundp->nodep()->castCell() : NULL;
|
||||
AstCell* cellp = foundp ? VN_CAST(foundp->nodep(), Cell) : NULL;
|
||||
if (!cellp) {
|
||||
nodep->v3error("In defparam, cell "<<nodep->path()<<" never declared");
|
||||
} else {
|
||||
|
|
@ -1165,7 +1162,7 @@ private:
|
|||
// Need to set pin numbers after varnames are created
|
||||
// But before we do the final resolution based on names
|
||||
VSymEnt* foundp = m_statep->getNodeSym(m_modp)->findIdFlat(nodep->name());
|
||||
AstVar* refp = foundp ? foundp->nodep()->castVar() : NULL;
|
||||
AstVar* refp = foundp ? VN_CAST(foundp->nodep(), Var) : NULL;
|
||||
if (!refp) {
|
||||
nodep->v3error("Input/output/inout declaration not found for port: "<<nodep->prettyName());
|
||||
} else if (!refp->isIO() && !refp->isIfaceRef()) {
|
||||
|
|
@ -1184,19 +1181,19 @@ private:
|
|||
// We used to nodep->allowImplicit() here, but it turns out
|
||||
// normal "assigns" can also make implicit wires. Yuk.
|
||||
pinImplicitExprRecurse(nodep->lhsp());
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) {
|
||||
// tran gates need implicit creation
|
||||
// As VarRefs don't exist in forPrimary, sanity check
|
||||
if (m_statep->forPrimary()) nodep->v3fatalSrc("Assign aliases unexpected pre-dot");
|
||||
if (AstVarRef* forrefp = nodep->lhsp()->castVarRef()) {
|
||||
if (AstVarRef* forrefp = VN_CAST(nodep->lhsp(), VarRef)) {
|
||||
pinImplicitExprRecurse(forrefp);
|
||||
}
|
||||
if (AstVarRef* forrefp = nodep->rhsp()->castVarRef()) {
|
||||
if (AstVarRef* forrefp = VN_CAST(nodep->rhsp(), VarRef)) {
|
||||
pinImplicitExprRecurse(forrefp);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstImplicit* nodep) {
|
||||
// Unsupported gates need implicit creation
|
||||
|
|
@ -1206,7 +1203,7 @@ private:
|
|||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -1216,7 +1213,7 @@ public:
|
|||
m_statep = statep;
|
||||
m_modp = NULL;
|
||||
//
|
||||
rootp->accept(*this);
|
||||
iterate(rootp);
|
||||
}
|
||||
virtual ~LinkDotParamVisitor() {}
|
||||
};
|
||||
|
|
@ -1236,7 +1233,7 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
|||
// VISITs
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
// Recurse..., backward as must do packages before using packages
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
iterateChildrenBackwards(nodep);
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
UINFO(8," SCOPE "<<nodep<<endl);
|
||||
|
|
@ -1245,7 +1242,7 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
|||
// up with the hierarchy created by the CELL names.
|
||||
m_modSymp = m_statep->getScopeSym(nodep);
|
||||
m_scopep = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modSymp = NULL;
|
||||
m_scopep = NULL;
|
||||
}
|
||||
|
|
@ -1285,19 +1282,19 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
|||
// Track aliases created by V3Inline; if we get a VARXREF(aliased_from)
|
||||
// we'll need to replace it with a VARXREF(aliased_to)
|
||||
if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\talias: ");
|
||||
AstVarScope* fromVscp = nodep->lhsp()->castVarRef()->varScopep();
|
||||
AstVarScope* toVscp = nodep->rhsp()->castVarRef()->varScopep();
|
||||
AstVarScope* fromVscp = VN_CAST(nodep->lhsp(), VarRef)->varScopep();
|
||||
AstVarScope* toVscp = VN_CAST(nodep->rhsp(), VarRef)->varScopep();
|
||||
if (!fromVscp || !toVscp) nodep->v3fatalSrc("Bad alias scopes");
|
||||
fromVscp->user2p(toVscp);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignVarScope* nodep) {
|
||||
UINFO(5,"ASSIGNVARSCOPE "<<nodep<<endl);
|
||||
if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\tavs: ");
|
||||
VSymEnt* rhsSymp;
|
||||
{
|
||||
AstVarRef* refp = nodep->rhsp()->castVarRef();
|
||||
AstVarXRef* xrefp = nodep->rhsp()->castVarXRef();
|
||||
AstVarRef* refp = VN_CAST(nodep->rhsp(), VarRef);
|
||||
AstVarXRef* xrefp = VN_CAST(nodep->rhsp(), VarXRef);
|
||||
if (!refp && !xrefp) nodep->v3fatalSrc("Unsupported: Non Var(X)Ref attached to interface pin");
|
||||
string inl = (xrefp && xrefp->inlinedDots().size()) ? (xrefp->inlinedDots() + "__DOT__") : "";
|
||||
VSymEnt* symp = NULL;
|
||||
|
|
@ -1317,8 +1314,8 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
|||
}
|
||||
VSymEnt* lhsSymp;
|
||||
{
|
||||
AstVarXRef* xrefp = nodep->lhsp()->castVarXRef();
|
||||
AstVarRef* refp = nodep->lhsp()->castVarRef();
|
||||
const AstVarXRef* xrefp = VN_CAST(nodep->lhsp(), VarXRef);
|
||||
const AstVarRef* refp = VN_CAST(nodep->lhsp(), VarRef);
|
||||
|
||||
if (!refp && !xrefp) nodep->v3fatalSrc("Unsupported: Non Var(X)Ref attached to interface pin");
|
||||
string scopename = refp ? refp->varp()->name() : xrefp->dotted()+"."+xrefp->name();
|
||||
|
|
@ -1341,7 +1338,7 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
|||
virtual void visit(AstNodeMath*) {}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -1352,7 +1349,7 @@ public:
|
|||
m_scopep = NULL;
|
||||
m_statep = statep;
|
||||
//
|
||||
rootp->accept(*this);
|
||||
iterate(rootp);
|
||||
}
|
||||
virtual ~LinkDotScopeVisitor() {}
|
||||
};
|
||||
|
|
@ -1377,18 +1374,18 @@ class LinkDotIfaceVisitor : public AstNVisitor {
|
|||
// Create symbol table for the vars
|
||||
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, NULL);
|
||||
m_curSymp->fallbackp(oldCurSymp);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_curSymp = oldCurSymp;
|
||||
}
|
||||
virtual void visit(AstModportFTaskRef* nodep) {
|
||||
UINFO(5," fif: "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->isExport()) nodep->v3error("Unsupported: modport export");
|
||||
VSymEnt* symp = m_curSymp->findIdFallback(nodep->name());
|
||||
if (!symp) {
|
||||
nodep->v3error("Modport item not found: "<<nodep->prettyName());
|
||||
} else if (AstNodeFTask* ftaskp = symp->nodep()->castNodeFTask()) {
|
||||
} else if (AstNodeFTask* ftaskp = VN_CAST(symp->nodep(), NodeFTask)) {
|
||||
// Make symbol under modport that points at the _interface_'s var, not the modport.
|
||||
nodep->ftaskp(ftaskp);
|
||||
VSymEnt* subSymp = m_statep->insertSym(m_curSymp, nodep->name(), ftaskp, NULL/*package*/);
|
||||
|
|
@ -1404,16 +1401,16 @@ class LinkDotIfaceVisitor : public AstNVisitor {
|
|||
}
|
||||
virtual void visit(AstModportVarRef* nodep) {
|
||||
UINFO(5," fiv: "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
VSymEnt* symp = m_curSymp->findIdFallback(nodep->name());
|
||||
if (!symp) {
|
||||
nodep->v3error("Modport item not found: "<<nodep->prettyName());
|
||||
} else if (AstVar* varp = symp->nodep()->castVar()) {
|
||||
} else if (AstVar* varp = VN_CAST(symp->nodep(), Var)) {
|
||||
// Make symbol under modport that points at the _interface_'s var via the modport.
|
||||
// (Need modport still to test input/output markings)
|
||||
nodep->varp(varp);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), nodep, NULL/*package*/);
|
||||
} else if (AstVarScope* vscp = symp->nodep()->castVarScope()) {
|
||||
} else if (AstVarScope* vscp = VN_CAST(symp->nodep(), VarScope)) {
|
||||
// Make symbol under modport that points at the _interface_'s var, not the modport.
|
||||
nodep->varp(vscp->varp());
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), vscp, NULL/*package*/);
|
||||
|
|
@ -1428,7 +1425,7 @@ class LinkDotIfaceVisitor : public AstNVisitor {
|
|||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -1437,7 +1434,7 @@ public:
|
|||
UINFO(4,__FUNCTION__<<": "<<endl);
|
||||
m_curSymp = curSymp;
|
||||
m_statep = statep;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~LinkDotIfaceVisitor() {}
|
||||
};
|
||||
|
|
@ -1499,7 +1496,7 @@ private:
|
|||
}
|
||||
string ascii() const {
|
||||
static const char* const names[] = { "NONE","PACKAGE","SCOPE","FINAL","MEMBER" };
|
||||
ostringstream sstr;
|
||||
std::ostringstream sstr;
|
||||
sstr<<"ds="<<names[m_dotPos];
|
||||
sstr<<" dse"<<(void*)m_dotSymp;
|
||||
sstr<<" txt="<<m_dotText;
|
||||
|
|
@ -1534,10 +1531,10 @@ private:
|
|||
// Return a variable if possible, auto converting a modport to variable
|
||||
if (!symp) {
|
||||
return NULL;
|
||||
} else if (symp->nodep()->castVar()) {
|
||||
return symp->nodep()->castVar();
|
||||
} else if (symp->nodep()->castModportVarRef()) {
|
||||
AstModportVarRef* snodep = symp->nodep()->castModportVarRef();
|
||||
} else if (VN_IS(symp->nodep(), Var)) {
|
||||
return VN_CAST(symp->nodep(), Var);
|
||||
} else if (VN_IS(symp->nodep(), ModportVarRef)) {
|
||||
AstModportVarRef* snodep = VN_CAST(symp->nodep(), ModportVarRef);
|
||||
AstVar* varp = snodep->varp();
|
||||
if (lvalue && snodep->isInput()) {
|
||||
nodep->v3error("Attempt to drive input-only modport: "<<nodep->prettyName());
|
||||
|
|
@ -1548,8 +1545,8 @@ private:
|
|||
}
|
||||
}
|
||||
void taskFuncSwapCheck(AstNodeFTaskRef* nodep) {
|
||||
if (nodep->taskp() && nodep->taskp()->castTask()
|
||||
&& nodep->castFuncRef()) nodep->v3error("Illegal call of a task as a function: "<<nodep->prettyName());
|
||||
if (nodep->taskp() && VN_IS(nodep->taskp(), Task)
|
||||
&& VN_IS(nodep, FuncRef)) nodep->v3error("Illegal call of a task as a function: "<<nodep->prettyName());
|
||||
}
|
||||
inline void checkNoDot(AstNode* nodep) {
|
||||
if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) {
|
||||
|
|
@ -1582,7 +1579,7 @@ private:
|
|||
// VISITs
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
// Recurse..., backward as must do packages before using packages
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
iterateChildrenBackwards(nodep);
|
||||
}
|
||||
virtual void visit(AstTypeTable* nodep) {}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
|
|
@ -1594,7 +1591,7 @@ private:
|
|||
m_cellp = NULL;
|
||||
m_modp = nodep;
|
||||
m_modportNum = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL;
|
||||
}
|
||||
|
|
@ -1604,7 +1601,7 @@ private:
|
|||
VSymEnt* oldCurSymp = m_curSymp;
|
||||
checkNoDot(nodep);
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = m_statep->getScopeSym(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL;
|
||||
m_modSymp = oldModSymp;
|
||||
m_curSymp = oldCurSymp;
|
||||
|
|
@ -1624,7 +1621,7 @@ private:
|
|||
nodep->v3fatalSrc("Cell has unlinked module"); // V3LinkCell should have errored out
|
||||
}
|
||||
else {
|
||||
if (nodep->modp()->castNotFoundModule()) {
|
||||
if (VN_IS(nodep->modp(), NotFoundModule)) {
|
||||
// Prevent warnings about missing pin connects
|
||||
if (nodep->pinsp()) nodep->pinsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
|
|
@ -1636,7 +1633,7 @@ private:
|
|||
UINFO(4,"(Backto) Link Cell: "<<nodep<<endl);
|
||||
//if (debug()) { nodep->dumpTree(cout,"linkcell:"); }
|
||||
//if (debug()) { nodep->modp()->dumpTree(cout,"linkcemd:"); }
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_pinSymp = NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -1647,20 +1644,20 @@ private:
|
|||
virtual void visit(AstPin* nodep) {
|
||||
// Pin: Link to submodule's port
|
||||
checkNoDot(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->modVarp()) {
|
||||
if (!m_pinSymp) nodep->v3fatalSrc("Pin not under cell?");
|
||||
VSymEnt* foundp = m_pinSymp->findIdFlat(nodep->name());
|
||||
const char* whatp = nodep->param() ? "parameter pin" : "pin";
|
||||
if (!foundp) {
|
||||
if (nodep->name() == "__paramNumber1" && m_cellp->modp()->castPrimitive()) {
|
||||
if (nodep->name() == "__paramNumber1" && VN_IS(m_cellp->modp(), Primitive)) {
|
||||
// Primitive parameter is really a delay we can just ignore
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
return;
|
||||
}
|
||||
nodep->v3error(ucfirst(whatp)<<" not found: "<<nodep->prettyName());
|
||||
}
|
||||
else if (AstVar* refp = foundp->nodep()->castVar()) {
|
||||
else if (AstVar* refp = VN_CAST(foundp->nodep(), Var)) {
|
||||
if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) {
|
||||
nodep->v3error(ucfirst(whatp)<<" is not an in/out/inout/param/interface: "<<nodep->prettyName());
|
||||
} else {
|
||||
|
|
@ -1668,7 +1665,7 @@ private:
|
|||
markAndCheckPinDup(nodep, refp, whatp);
|
||||
}
|
||||
}
|
||||
else if (AstParamTypeDType* refp = foundp->nodep()->castParamTypeDType()) {
|
||||
else if (AstParamTypeDType* refp = VN_CAST(foundp->nodep(), ParamTypeDType)) {
|
||||
nodep->modPTypep(refp);
|
||||
markAndCheckPinDup(nodep, refp, whatp);
|
||||
}
|
||||
|
|
@ -1696,20 +1693,20 @@ private:
|
|||
m_ds.m_dotPos = DP_SCOPE;
|
||||
|
||||
// m_ds.m_dotText communicates the cell prefix between stages
|
||||
if (nodep->lhsp()->castPackageRef()) {
|
||||
if (VN_IS(nodep->lhsp(), PackageRef)) {
|
||||
//if (!start) { nodep->lhsp()->v3error("Package reference may not be embedded in dotted reference"); m_ds.m_dotErr=true; }
|
||||
m_ds.m_dotPos = DP_PACKAGE;
|
||||
} else {
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
//if (debug()>=9) nodep->dumpTree("-dot-lho: ");
|
||||
}
|
||||
if (m_ds.m_unresolved && (nodep->lhsp()->castCellRef() || nodep->lhsp()->castCellArrayRef())) {
|
||||
if (m_ds.m_unresolved && (VN_IS(nodep->lhsp(), CellRef) || VN_IS(nodep->lhsp(), CellArrayRef))) {
|
||||
m_ds.m_unlinkedScope = nodep->lhsp();
|
||||
}
|
||||
if (!m_ds.m_dotErr) { // Once something wrong, give up
|
||||
if (start && m_ds.m_dotPos==DP_SCOPE) m_ds.m_dotPos = DP_FINAL; // Top 'final' dot RHS is final RHS, else it's a DOT(DOT(x,*here*),real-rhs) which we consider a RHS
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
//if (debug()>=9) nodep->dumpTree("-dot-rho: ");
|
||||
}
|
||||
if (start) {
|
||||
|
|
@ -1770,8 +1767,8 @@ private:
|
|||
expectWhat = "scope/variable";
|
||||
allowScope = true;
|
||||
allowVar = true;
|
||||
if (!m_ds.m_dotp->lhsp()->castPackageRef()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
packagep = m_ds.m_dotp->lhsp()->castPackageRef()->packagep();
|
||||
if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
packagep = VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep();
|
||||
if (!packagep) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(packagep);
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
|
|
@ -1803,9 +1800,9 @@ private:
|
|||
// What fell out?
|
||||
bool ok = false;
|
||||
if (!foundp) {
|
||||
} else if (foundp->nodep()->castCell()
|
||||
|| foundp->nodep()->castBegin()
|
||||
|| foundp->nodep()->castModule()) { // if top
|
||||
} else if (VN_IS(foundp->nodep(), Cell)
|
||||
|| VN_IS(foundp->nodep(), Begin)
|
||||
|| VN_IS(foundp->nodep(), Module)) { // if top
|
||||
if (allowScope) {
|
||||
ok = true;
|
||||
m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name());
|
||||
|
|
@ -1813,17 +1810,17 @@ private:
|
|||
m_ds.m_dotPos = DP_SCOPE;
|
||||
// Upper AstDot visitor will handle it from here
|
||||
}
|
||||
else if (foundp->nodep()->castCell()
|
||||
else if (VN_IS(foundp->nodep(), Cell)
|
||||
&& allowVar && m_cellp) {
|
||||
AstCell* cellp = foundp->nodep()->castCell();
|
||||
if (cellp->modp()->castIface()) {
|
||||
AstCell* cellp = VN_CAST(foundp->nodep(), Cell);
|
||||
if (VN_IS(cellp->modp(), Iface)) {
|
||||
// Interfaces can be referenced like a variable for interconnect
|
||||
VSymEnt* cellEntp = m_statep->getNodeSym(cellp);
|
||||
if (!cellEntp) nodep->v3fatalSrc("No interface sym entry");
|
||||
VSymEnt* parentEntp = cellEntp->parentp(); // Container of the var; probably a module or generate begin
|
||||
string findName = nodep->name()+"__Viftop";
|
||||
VSymEnt* ifaceSymp = parentEntp->findIdFallback(findName);
|
||||
AstVar* ifaceRefVarp = ifaceSymp ? ifaceSymp->nodep()->castVar() : NULL;
|
||||
AstVar* ifaceRefVarp = ifaceSymp ? VN_CAST(ifaceSymp->nodep(), Var) : NULL;
|
||||
if (!ifaceRefVarp) nodep->v3fatalSrc("Can't find interface var ref: "<<findName);
|
||||
//
|
||||
ok = true;
|
||||
|
|
@ -1833,7 +1830,7 @@ private:
|
|||
UINFO(9," cell -> iface varref "<<foundp->nodep()<<endl);
|
||||
AstNode* newp = new AstVarRef(ifaceRefVarp->fileline(), ifaceRefVarp, false);
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
} else if (cellp->modp()->castNotFoundModule()) {
|
||||
} else if (VN_IS(cellp->modp(), NotFoundModule)) {
|
||||
cellp->v3error("Cannot find file containing interface: " << AstNode::prettyName(cellp->modp()->name()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1859,7 +1856,7 @@ private:
|
|||
refp->varp(varp);
|
||||
m_ds.m_dotText = "";
|
||||
if (m_ds.m_unresolved && m_ds.m_unlinkedScope) {
|
||||
newp = new AstUnlinkedRef(nodep->fileline(), refp->castVarXRef(),
|
||||
newp = new AstUnlinkedRef(nodep->fileline(), VN_CAST(refp, VarXRef),
|
||||
refp->name(), m_ds.m_unlinkedScope->unlinkFrBack());
|
||||
m_ds.m_unlinkedScope = NULL;
|
||||
m_ds.m_unresolved = false;
|
||||
|
|
@ -1877,23 +1874,23 @@ private:
|
|||
ok = true;
|
||||
}
|
||||
}
|
||||
else if (AstModport* modportp = foundp->nodep()->castModport()) {
|
||||
else if (AstModport* modportp = VN_CAST(foundp->nodep(), Modport)) {
|
||||
// A scope reference into an interface's modport (not necessarily at a pin connection)
|
||||
UINFO(9,"cell-ref-to-modport "<<m_ds.m_dotText<<" "<<nodep<<endl);
|
||||
UINFO(9,"dotSymp "<<m_ds.m_dotSymp<<" "<<m_ds.m_dotSymp->nodep()<<endl);
|
||||
// Iface was the previously dotted component
|
||||
if (!m_ds.m_dotSymp
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()->castIface()) {
|
||||
|| !VN_IS(m_ds.m_dotSymp->nodep(), Cell)
|
||||
|| !VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp()
|
||||
|| !VN_IS(VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp(), Iface)) {
|
||||
nodep->v3error("Modport not referenced as <interface>."<<modportp->prettyName());
|
||||
} else if (!m_ds.m_dotSymp->nodep()->castCell()->modp()
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()->castIface()) {
|
||||
} else if (!VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp()
|
||||
|| !VN_IS(VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp(), Iface)) {
|
||||
nodep->v3error("Modport not referenced from underneath an interface: "<<modportp->prettyName());
|
||||
} else {
|
||||
AstCell* cellp = m_ds.m_dotSymp->nodep()->castCell();
|
||||
AstCell* cellp = VN_CAST(m_ds.m_dotSymp->nodep(), Cell);
|
||||
if (!cellp) nodep->v3fatalSrc("Modport not referenced from a cell");
|
||||
AstIface* ifacep = cellp->modp()->castIface();
|
||||
AstIface* ifacep = VN_CAST(cellp->modp(), Iface);
|
||||
//string cellName = m_ds.m_dotText; // Use cellp->name
|
||||
m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name());
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(modportp);
|
||||
|
|
@ -1904,7 +1901,7 @@ private:
|
|||
nodep->replaceWith(refp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
else if (AstEnumItem* valuep = foundp->nodep()->castEnumItem()) {
|
||||
else if (AstEnumItem* valuep = VN_CAST(foundp->nodep(), EnumItem)) {
|
||||
if (allowVar) {
|
||||
AstNode* newp = new AstEnumItemRef(nodep->fileline(), valuep, foundp->packagep());
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
|
|
@ -1915,7 +1912,7 @@ private:
|
|||
//
|
||||
if (!ok) {
|
||||
// Cells/interfaces can't be implicit
|
||||
bool isCell = foundp ? foundp->nodep()->castCell() != NULL : false;
|
||||
bool isCell = foundp ? VN_IS(foundp->nodep(), Cell) : false;
|
||||
bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText=="" && !isCell);
|
||||
bool err = !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name()));
|
||||
if (err) {
|
||||
|
|
@ -1952,7 +1949,7 @@ private:
|
|||
// ParseRefs are used the first pass (forPrimary) so we shouldn't get can't find
|
||||
// errors here now that we have a VarRef.
|
||||
// No checkNoDot; created and iterated from a parseRef
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->varp()) {
|
||||
UINFO(9," linkVarRef se"<<(void*)m_curSymp<<" n="<<nodep<<endl);
|
||||
if (!m_curSymp) nodep->v3fatalSrc("NULL lookup symbol table");
|
||||
|
|
@ -2001,7 +1998,7 @@ private:
|
|||
// V3Inst may have expanded arrays of interfaces to AstVarXRef's even though they are in the same module
|
||||
// detect this and convert to normal VarRefs
|
||||
if (!m_statep->forPrearray() && !m_statep->forScopeCreation()) {
|
||||
if (nodep->dtypep()->castIfaceRefDType()) {
|
||||
if (VN_IS(nodep->dtypep(), IfaceRefDType)) {
|
||||
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), nodep->varp(), nodep->lvalue());
|
||||
nodep->replaceWith(newrefp);
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
|
|
@ -2010,7 +2007,7 @@ private:
|
|||
} else {
|
||||
string baddot;
|
||||
VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
|
||||
AstVarScope* vscp = foundp ? foundp->nodep()->castVarScope() : NULL;
|
||||
AstVarScope* vscp = foundp ? VN_CAST(foundp->nodep(), VarScope) : NULL;
|
||||
if (!vscp) {
|
||||
nodep->v3error("Can't find varpin scope of '"<<baddot
|
||||
<<"' in dotted signal: "<<nodep->dotted()+"."+nodep->prettyName());
|
||||
|
|
@ -2018,7 +2015,7 @@ private:
|
|||
} else {
|
||||
while (vscp->user2p()) { // If V3Inline aliased it, pick up the new signal
|
||||
UINFO(7," Resolved pre-alias "<<vscp<<endl); // Also prints taskp
|
||||
vscp = vscp->user2p()->castVarScope();
|
||||
vscp = VN_CAST(vscp->user2p(), VarScope);
|
||||
}
|
||||
// Convert the VarXRef to a VarRef, so we don't need later optimizations to deal with VarXRef.
|
||||
nodep->varp(vscp->varp());
|
||||
|
|
@ -2034,20 +2031,20 @@ private:
|
|||
}
|
||||
virtual void visit(AstEnumItemRef* nodep) {
|
||||
// EnumItemRef may be under a dot. Should already be resolved.
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstMethodSel* nodep) {
|
||||
// Created here so should already be resolved.
|
||||
DotStates lastStates = m_ds;
|
||||
{
|
||||
m_ds.init(m_curSymp);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_ds = lastStates;
|
||||
}
|
||||
virtual void visit(AstVar* nodep) {
|
||||
checkNoDot(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (m_statep->forPrimary() && nodep->isIO() && !m_ftaskp && !nodep->user4()) {
|
||||
nodep->v3error("Input/output/inout does not appear in port list: "<<nodep->prettyName());
|
||||
}
|
||||
|
|
@ -2056,9 +2053,9 @@ private:
|
|||
if (nodep->user3SetOnce()) return;
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) {
|
||||
if (!m_ds.m_dotp->lhsp()->castPackageRef()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
if (!m_ds.m_dotp->lhsp()->castPackageRef()->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
nodep->packagep(m_ds.m_dotp->lhsp()->castPackageRef()->packagep());
|
||||
if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
if (!VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
nodep->packagep(VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep());
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
m_ds.m_dotp = NULL;
|
||||
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) {
|
||||
|
|
@ -2113,7 +2110,7 @@ private:
|
|||
dotSymp = m_statep->findDotted(dotSymp, nodep->dotted(), baddot, okSymp); // Maybe NULL
|
||||
}
|
||||
VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
|
||||
AstNodeFTask* taskp = foundp ? foundp->nodep()->castNodeFTask() : NULL; // Maybe NULL
|
||||
AstNodeFTask* taskp = foundp ? VN_CAST(foundp->nodep(), NodeFTask) : NULL; // Maybe NULL
|
||||
if (taskp) {
|
||||
nodep->taskp(taskp);
|
||||
nodep->packagep(foundp->packagep());
|
||||
|
|
@ -2138,13 +2135,13 @@ private:
|
|||
DotStates lastStates = m_ds;
|
||||
{
|
||||
m_ds.init(m_curSymp);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_ds = lastStates;
|
||||
}
|
||||
virtual void visit(AstSelBit* nodep) {
|
||||
if (nodep->user3SetOnce()) return;
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
if (m_ds.m_dotPos == DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart}
|
||||
UINFO(9," deferring until after a V3Param pass: "<<nodep<<endl);
|
||||
m_ds.m_dotText += "__BRA__??__KET__";
|
||||
|
|
@ -2152,12 +2149,12 @@ private:
|
|||
// And pass up m_ds.m_dotText
|
||||
}
|
||||
// Pass dot state down to fromp()
|
||||
nodep->fromp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->fromp());
|
||||
DotStates lastStates = m_ds;
|
||||
{
|
||||
m_ds.init(m_curSymp);
|
||||
nodep->bitp()->iterateAndNext(*this);
|
||||
nodep->attrp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->bitp());
|
||||
iterateAndNextNull(nodep->attrp());
|
||||
}
|
||||
m_ds = lastStates;
|
||||
if (m_ds.m_unresolved && m_ds.m_dotPos == DP_SCOPE) {
|
||||
|
|
@ -2174,19 +2171,19 @@ private:
|
|||
m_ds.m_dotErr = true;
|
||||
return;
|
||||
}
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
DotStates lastStates = m_ds;
|
||||
{
|
||||
m_ds.init(m_curSymp);
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
nodep->thsp()->iterateAndNext(*this);
|
||||
nodep->attrp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
iterateAndNextNull(nodep->thsp());
|
||||
iterateAndNextNull(nodep->attrp());
|
||||
}
|
||||
m_ds = lastStates;
|
||||
}
|
||||
virtual void visit(AstMemberSel* nodep) {
|
||||
// checkNoDot not appropriate, can be under a dot
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) {
|
||||
UINFO(5," "<<nodep<<endl);
|
||||
|
|
@ -2195,7 +2192,7 @@ private:
|
|||
{
|
||||
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
|
||||
UINFO(5," cur=se"<<(void*)m_curSymp<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_ds.m_dotSymp = m_curSymp = oldCurSymp;
|
||||
UINFO(5," cur=se"<<(void*)m_curSymp<<endl);
|
||||
|
|
@ -2207,7 +2204,7 @@ private:
|
|||
{
|
||||
m_ftaskp = nodep;
|
||||
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_ds.m_dotSymp = m_curSymp = oldCurSymp;
|
||||
m_ftaskp = NULL;
|
||||
|
|
@ -2216,9 +2213,9 @@ private:
|
|||
// Resolve its reference
|
||||
if (nodep->user3SetOnce()) return;
|
||||
if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) {
|
||||
if (!m_ds.m_dotp->lhsp()->castPackageRef()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
if (!m_ds.m_dotp->lhsp()->castPackageRef()->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
nodep->packagep(m_ds.m_dotp->lhsp()->castPackageRef()->packagep());
|
||||
if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
if (!VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link");
|
||||
nodep->packagep(VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep());
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
m_ds.m_dotp = NULL;
|
||||
} else {
|
||||
|
|
@ -2231,11 +2228,11 @@ private:
|
|||
} else {
|
||||
foundp = m_curSymp->findIdFallback(nodep->name());
|
||||
}
|
||||
if (AstTypedef* defp = foundp ? foundp->nodep()->castTypedef() : NULL) {
|
||||
if (AstTypedef* defp = foundp ? VN_CAST(foundp->nodep(), Typedef) : NULL) {
|
||||
nodep->refDTypep(defp->subDTypep());
|
||||
nodep->packagep(foundp->packagep());
|
||||
}
|
||||
else if (AstParamTypeDType* defp = foundp ? foundp->nodep()->castParamTypeDType() : NULL) {
|
||||
else if (AstParamTypeDType* defp = foundp ? VN_CAST(foundp->nodep(), ParamTypeDType) : NULL) {
|
||||
nodep->refDTypep(defp);
|
||||
nodep->packagep(foundp->packagep());
|
||||
}
|
||||
|
|
@ -2243,14 +2240,14 @@ private:
|
|||
nodep->v3error("Can't find typedef: "<<nodep->prettyName());
|
||||
}
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstDpiExport* nodep) {
|
||||
// AstDpiExport: Make sure the function referenced exists, then dump it
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
checkNoDot(nodep);
|
||||
VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name());
|
||||
AstNodeFTask* taskp = foundp ? foundp->nodep()->castNodeFTask() : NULL;
|
||||
AstNodeFTask* taskp = foundp ? VN_CAST(foundp->nodep(), NodeFTask) : NULL;
|
||||
if (!taskp) { nodep->v3error("Can't find definition of exported task/function: "<<nodep->prettyName()); }
|
||||
else if (taskp->dpiExport()) {
|
||||
nodep->v3error("Function was already DPI Exported, duplicate not allowed: "<<nodep->prettyName());
|
||||
|
|
@ -2277,7 +2274,7 @@ private:
|
|||
}
|
||||
virtual void visit(AstCellRef* nodep) {
|
||||
UINFO(5," AstCellRef: "<<nodep<<" "<<m_ds.ascii()<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCellArrayRef* nodep) {
|
||||
UINFO(5," AstCellArrayRef: "<<nodep<<" "<<m_ds.ascii()<<endl);
|
||||
|
|
@ -2291,7 +2288,7 @@ private:
|
|||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
checkNoDot(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
|
|
@ -2306,7 +2303,7 @@ public:
|
|||
m_ftaskp = NULL;
|
||||
m_modportNum = 0;
|
||||
//
|
||||
rootp->accept(*this);
|
||||
iterate(rootp);
|
||||
}
|
||||
virtual ~LinkDotResolveVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
class LinkJumpVisitor : public AstNVisitor {
|
||||
private:
|
||||
// TYPES
|
||||
typedef vector<AstBegin*> BeginStack;
|
||||
typedef std::vector<AstBegin*> BeginStack;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
|
|
@ -54,25 +54,21 @@ private:
|
|||
BeginStack m_beginStack; // All begin blocks above current node
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) {
|
||||
// Put label under given node, and if WHILE optionally at end of iteration
|
||||
UINFO(4,"Create label for "<<nodep<<endl);
|
||||
if (nodep->castJumpLabel()) return nodep->castJumpLabel(); // Done
|
||||
if (VN_IS(nodep, JumpLabel)) return VN_CAST(nodep, JumpLabel); // Done
|
||||
|
||||
AstNode* underp = NULL;
|
||||
bool under_and_next = true;
|
||||
if (nodep->castBegin()) underp = nodep->castBegin()->stmtsp();
|
||||
else if (nodep->castNodeFTask()) underp = nodep->castNodeFTask()->stmtsp();
|
||||
else if (nodep->castWhile()) {
|
||||
if (VN_IS(nodep, Begin)) underp = VN_CAST(nodep, Begin)->stmtsp();
|
||||
else if (VN_IS(nodep, NodeFTask)) underp = VN_CAST(nodep, NodeFTask)->stmtsp();
|
||||
else if (VN_IS(nodep, While)) {
|
||||
if (endOfIter) {
|
||||
// Note we jump to end of bodysp; a FOR loop has its increment under incsp() which we don't skip
|
||||
underp = nodep->castWhile()->bodysp();
|
||||
underp = VN_CAST(nodep, While)->bodysp();
|
||||
} else {
|
||||
underp = nodep; under_and_next=false; // IE we skip the entire while
|
||||
}
|
||||
|
|
@ -84,14 +80,14 @@ private:
|
|||
// Skip over variables as we'll just move them in a momement
|
||||
// Also this would otherwise prevent us from using a label twice
|
||||
// see t_func_return test.
|
||||
while (underp && underp->castVar()) underp = underp->nextp();
|
||||
while (underp && VN_IS(underp, Var)) underp = underp->nextp();
|
||||
if (underp) UINFO(5," Underpoint is "<<underp<<endl);
|
||||
|
||||
if (!underp) {
|
||||
nodep->v3fatalSrc("Break/disable/continue not under expected statement");
|
||||
return NULL;
|
||||
} else if (underp->castJumpLabel()) {
|
||||
return underp->castJumpLabel();
|
||||
} else if (VN_IS(underp, JumpLabel)) {
|
||||
return VN_CAST(underp, JumpLabel);
|
||||
} else { // Move underp stuff to be under a new label
|
||||
AstJumpLabel* labelp = new AstJumpLabel(nodep->fileline(), NULL);
|
||||
|
||||
|
|
@ -104,7 +100,7 @@ private:
|
|||
// Keep any AstVars under the function not under the new JumpLabel
|
||||
for (AstNode* nextp, *varp=underp; varp; varp = nextp) {
|
||||
nextp = varp->nextp();
|
||||
if (varp->castVar()) {
|
||||
if (VN_IS(varp, Var)) {
|
||||
labelp->addPrev(varp->unlinkFrBack());
|
||||
}
|
||||
}
|
||||
|
|
@ -117,18 +113,18 @@ private:
|
|||
if (nodep->dead()) return;
|
||||
m_modp = nodep;
|
||||
m_repeatNum = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep) {
|
||||
m_ftaskp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_ftaskp = NULL;
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) {
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
m_beginStack.push_back(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_beginStack.pop_back();
|
||||
}
|
||||
virtual void visit(AstRepeat* nodep) {
|
||||
|
|
@ -167,17 +163,17 @@ private:
|
|||
bool lastInc = m_loopInc;
|
||||
m_loopp = nodep;
|
||||
m_loopInc = false;
|
||||
nodep->precondsp()->iterateAndNext(*this);
|
||||
nodep->condp()->iterateAndNext(*this);
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->precondsp());
|
||||
iterateAndNextNull(nodep->condp());
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
m_loopInc = true;
|
||||
nodep->incsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->incsp());
|
||||
m_loopInc = lastInc;
|
||||
m_loopp = lastLoopp;
|
||||
}
|
||||
virtual void visit(AstReturn* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
AstFunc* funcp = m_ftaskp->castFunc();
|
||||
iterateChildren(nodep);
|
||||
AstFunc* funcp = VN_CAST(m_ftaskp, Func);
|
||||
if (!m_ftaskp) { nodep->v3error("Return isn't underneath a task or function"); }
|
||||
else if (funcp && !nodep->lhsp()) { nodep->v3error("Return underneath a function should have return value"); }
|
||||
else if (!funcp && nodep->lhsp()) { nodep->v3error("Return underneath a task shouldn't have return value"); }
|
||||
|
|
@ -185,7 +181,7 @@ private:
|
|||
if (funcp && nodep->lhsp()) {
|
||||
// Set output variable to return value
|
||||
nodep->addPrev(new AstAssign(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), funcp->fvarp()->castVar(), true),
|
||||
new AstVarRef(nodep->fileline(), VN_CAST(funcp->fvarp(), Var), true),
|
||||
nodep->lhsp()->unlinkFrBackWithNext()));
|
||||
}
|
||||
// Jump to the end of the function call
|
||||
|
|
@ -195,7 +191,7 @@ private:
|
|||
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstBreak* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!m_loopp) { nodep->v3error("break isn't underneath a loop"); }
|
||||
else {
|
||||
// Jump to the end of the loop
|
||||
|
|
@ -205,7 +201,7 @@ private:
|
|||
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstContinue* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!m_loopp) { nodep->v3error("continue isn't underneath a loop"); }
|
||||
else {
|
||||
// Jump to the end of this iteration
|
||||
|
|
@ -217,7 +213,7 @@ private:
|
|||
}
|
||||
virtual void visit(AstDisable* nodep) {
|
||||
UINFO(8," DISABLE "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
AstBegin* beginp = NULL;
|
||||
for (BeginStack::reverse_iterator it = m_beginStack.rbegin(); it != m_beginStack.rend(); ++it) {
|
||||
UINFO(9," UNDERBLK "<<*it<<endl);
|
||||
|
|
@ -242,7 +238,7 @@ private:
|
|||
|
||||
virtual void visit(AstConst* nodep) {}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
|
|
@ -252,7 +248,7 @@ public:
|
|||
m_loopp = NULL;
|
||||
m_loopInc = false;
|
||||
m_repeatNum = 0;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~LinkJumpVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -47,11 +47,7 @@ private:
|
|||
AstNodeFTask* m_ftaskp; // Function or task we're inside
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITs
|
||||
// Result handing
|
||||
|
|
@ -67,7 +63,7 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Nodes that start propagating down lvalues
|
||||
|
|
@ -76,19 +72,19 @@ private:
|
|||
// When the varref's were created, we didn't know the I/O state
|
||||
// Now that we do, and it's from a output, we know it's a lvalue
|
||||
m_setRefLvalue = true;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_setRefLvalue = false;
|
||||
} else {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
m_setRefLvalue = false;
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
|
|
@ -96,10 +92,10 @@ private:
|
|||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->filep());
|
||||
m_setRefLvalue = false;
|
||||
nodep->filenamep()->iterateAndNext(*this);
|
||||
nodep->modep()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->filenamep());
|
||||
iterateAndNextNull(nodep->modep());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
|
|
@ -107,7 +103,7 @@ private:
|
|||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->filep());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
|
|
@ -115,7 +111,7 @@ private:
|
|||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->filep());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
|
|
@ -123,7 +119,7 @@ private:
|
|||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->filep());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
|
|
@ -131,8 +127,8 @@ private:
|
|||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
nodep->strgp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->filep());
|
||||
iterateAndNextNull(nodep->strgp());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
|
|
@ -140,8 +136,8 @@ private:
|
|||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->filep()->iterateAndNext(*this);
|
||||
nodep->exprsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->filep());
|
||||
iterateAndNextNull(nodep->exprsp());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
|
|
@ -149,25 +145,25 @@ private:
|
|||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->exprsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->exprsp());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
virtual void visit(AstSysIgnore* nodep) {
|
||||
// Can't know if lvalue or not; presume so as stricter
|
||||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
virtual void visit(AstReadMem* nodep) {
|
||||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->memp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->memp());
|
||||
m_setRefLvalue = false;
|
||||
nodep->filenamep()->iterateAndNext(*this);
|
||||
nodep->lsbp()->iterateAndNext(*this);
|
||||
nodep->msbp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->filenamep());
|
||||
iterateAndNextNull(nodep->lsbp());
|
||||
iterateAndNextNull(nodep->msbp());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
|
|
@ -175,9 +171,9 @@ private:
|
|||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = false;
|
||||
nodep->searchp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->searchp());
|
||||
m_setRefLvalue = true;
|
||||
nodep->outp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->outp());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
|
|
@ -185,9 +181,9 @@ private:
|
|||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
m_setRefLvalue = true;
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
m_setRefLvalue = false;
|
||||
nodep->fmtp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->fmtp());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
|
|
@ -196,20 +192,20 @@ private:
|
|||
virtual void visit(AstSel* nodep) {
|
||||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
// Only set lvalues on the from
|
||||
m_setRefLvalue = false;
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
nodep->thsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
iterateAndNextNull(nodep->thsp());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
virtual void visit(AstNodeSel* nodep) {
|
||||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{ // Only set lvalues on the from
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
m_setRefLvalue = false;
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
|
|
@ -217,23 +213,23 @@ private:
|
|||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{ // selp is not an lvalue
|
||||
m_setRefLvalue = false;
|
||||
nodep->selp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->selp());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
virtual void visit(AstNodePreSel* nodep) {
|
||||
bool last_setRefLvalue = m_setRefLvalue;
|
||||
{ // Only set lvalues on the from
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
m_setRefLvalue = false;
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
nodep->thsp()->iterateAndNext(*this);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
iterateAndNextNull(nodep->thsp());
|
||||
}
|
||||
m_setRefLvalue = last_setRefLvalue;
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep) {
|
||||
m_ftaskp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_ftaskp = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep) {
|
||||
|
|
@ -242,13 +238,13 @@ private:
|
|||
// We'll deal with mismatching pins later
|
||||
if (!taskp) return;
|
||||
for (AstNode* stmtp = taskp->stmtsp(); stmtp && pinp; stmtp=stmtp->nextp()) {
|
||||
if (AstVar* portp = stmtp->castVar()) {
|
||||
if (const AstVar* portp = VN_CAST(stmtp, Var)) {
|
||||
if (portp->isIO()) {
|
||||
if (portp->isInput()) {
|
||||
pinp->iterate(*this);
|
||||
iterate(pinp);
|
||||
} else { // Output or Inout
|
||||
m_setRefLvalue = true;
|
||||
pinp->iterate(*this);
|
||||
iterate(pinp);
|
||||
m_setRefLvalue = false;
|
||||
}
|
||||
// Advance pin
|
||||
|
|
@ -260,7 +256,7 @@ private:
|
|||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -268,7 +264,7 @@ public:
|
|||
LinkLValueVisitor(AstNode* nodep, bool start) {
|
||||
m_setRefLvalue = start;
|
||||
m_ftaskp = NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~LinkLValueVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -52,11 +52,11 @@ void V3LinkLevel::modSortByLevel() {
|
|||
|
||||
// level() was computed for us in V3LinkCells
|
||||
|
||||
typedef vector<AstNodeModule*> ModVec;
|
||||
typedef std::vector<AstNodeModule*> ModVec;
|
||||
|
||||
ModVec vec;
|
||||
AstNodeModule* topp = NULL;
|
||||
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {
|
||||
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) {
|
||||
if (nodep->level()<=2) {
|
||||
if (topp) {
|
||||
nodep->v3warn(E_MULTITOP, "Unsupported: Multiple top level modules: "
|
||||
|
|
@ -108,7 +108,7 @@ void V3LinkLevel::wrapTop(AstNetlist* netlistp) {
|
|||
void V3LinkLevel::wrapTopCell(AstNetlist* netlistp) {
|
||||
AstNodeModule* newmodp = netlistp->modulesp();
|
||||
if (!newmodp || !newmodp->isTop()) netlistp->v3fatalSrc("No TOP module found to process");
|
||||
AstNodeModule* oldmodp = newmodp->nextp()->castNodeModule();
|
||||
AstNodeModule* oldmodp = VN_CAST(newmodp->nextp(), NodeModule);
|
||||
if (!oldmodp) netlistp->v3fatalSrc("No module found to process");
|
||||
|
||||
// Add instance
|
||||
|
|
@ -121,7 +121,7 @@ void V3LinkLevel::wrapTopCell(AstNetlist* netlistp) {
|
|||
|
||||
// Add pins
|
||||
for (AstNode* subnodep=oldmodp->stmtsp(); subnodep; subnodep = subnodep->nextp()) {
|
||||
if (AstVar* oldvarp=subnodep->castVar()) {
|
||||
if (AstVar* oldvarp=VN_CAST(subnodep, Var)) {
|
||||
UINFO(8,"VARWRAP "<<oldvarp<<endl);
|
||||
if (oldvarp->isIO()) {
|
||||
AstVar* varp = oldvarp->cloneTree(false);
|
||||
|
|
@ -154,8 +154,8 @@ void V3LinkLevel::wrapTopPackages(AstNetlist* netlistp) {
|
|||
// This way all later SCOPE based optimizations can ignore packages
|
||||
AstNodeModule* newmodp = netlistp->modulesp();
|
||||
if (!newmodp || !newmodp->isTop()) netlistp->v3fatalSrc("No TOP module found to process");
|
||||
for (AstNodeModule* modp = netlistp->modulesp(); modp; modp=modp->nextp()->castNodeModule()) {
|
||||
if (modp->castPackage()) {
|
||||
for (AstNodeModule* modp = netlistp->modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) {
|
||||
if (VN_IS(modp, Package)) {
|
||||
AstCell* cellp = new AstCell(modp->fileline(),
|
||||
// Could add __03a__03a="::" to prevent conflict
|
||||
// with module names/"v"
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ private:
|
|||
AstUser2InUse m_inuser2;
|
||||
|
||||
// TYPES
|
||||
typedef map <pair<void*,string>,AstTypedef*> ImplTypedefMap;
|
||||
typedef set <FileLine*> FileLineSet;
|
||||
typedef std::map<std::pair<void*,string>,AstTypedef*> ImplTypedefMap;
|
||||
typedef std::set<FileLine*> FileLineSet;
|
||||
|
||||
// STATE
|
||||
AstVar* m_varp; // Variable we're under
|
||||
|
|
@ -65,11 +65,7 @@ private:
|
|||
AstNodeDType* m_dtypep; // Current data type
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void cleanFileline(AstNode* nodep) {
|
||||
if (!nodep->user2SetOnce()) { // Process once
|
||||
|
|
@ -91,7 +87,7 @@ private:
|
|||
if (!nodep->user1SetOnce()) { // Process only once.
|
||||
cleanFileline(nodep);
|
||||
m_ftaskp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_ftaskp = NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -101,7 +97,7 @@ private:
|
|||
UINFO(5," "<<nodep<<endl);
|
||||
AstNodeModule* upperValueModp = m_valueModp;
|
||||
m_valueModp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_valueModp = upperValueModp;
|
||||
}
|
||||
}
|
||||
|
|
@ -110,17 +106,17 @@ private:
|
|||
cleanFileline(nodep);
|
||||
AstNodeDType* upperDtypep = m_dtypep;
|
||||
m_dtypep = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_dtypep = upperDtypep;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstEnumItem* nodep) {
|
||||
// Expand ranges
|
||||
cleanFileline(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->rangep()) {
|
||||
if (!nodep->rangep()->msbp()->castConst()
|
||||
|| !nodep->rangep()->lsbp()->castConst()) nodep->v3error("Enum ranges must be integral, per spec");
|
||||
if (!VN_IS(nodep->rangep()->msbp(), Const)
|
||||
|| !VN_IS(nodep->rangep()->lsbp(), Const)) nodep->v3error("Enum ranges must be integral, per spec");
|
||||
int msb = nodep->rangep()->msbConst();
|
||||
int lsb = nodep->rangep()->lsbConst();
|
||||
int increment = (msb > lsb) ? -1 : 1;
|
||||
|
|
@ -142,9 +138,9 @@ private:
|
|||
|
||||
virtual void visit(AstVar* nodep) {
|
||||
cleanFileline(nodep);
|
||||
if (nodep->subDTypep()->castParseTypeDType()) {
|
||||
if (VN_IS(nodep->subDTypep(), ParseTypeDType)) {
|
||||
// It's a parameter type. Use a different node type for this.
|
||||
AstNodeDType* dtypep = nodep->valuep()->castNodeDType();
|
||||
AstNodeDType* dtypep = VN_CAST(nodep->valuep(), NodeDType);
|
||||
if (!dtypep) {
|
||||
nodep->v3error("Parameter type's initial value isn't a type: "<<nodep->prettyName());
|
||||
nodep->unlinkFrBack();
|
||||
|
|
@ -166,7 +162,7 @@ private:
|
|||
nodep->trace(false);
|
||||
}
|
||||
m_varp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_varp = NULL;
|
||||
// temporaries under an always aren't expected to be blocking
|
||||
if (m_inAlways) nodep->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
|
||||
|
|
@ -202,9 +198,9 @@ private:
|
|||
|
||||
virtual void visit(AstAttrOf* nodep) {
|
||||
cleanFileline(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->attrType() == AstAttrType::DT_PUBLIC) {
|
||||
AstTypedef* typep = nodep->backp()->castTypedef();
|
||||
AstTypedef* typep = VN_CAST(nodep->backp(), Typedef);
|
||||
if (!typep) nodep->v3fatalSrc("Attribute not attached to typedef");
|
||||
typep->attrPublic(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
|
|
@ -270,7 +266,7 @@ private:
|
|||
// AlwaysPublic was attached under a var, but it's a statement that should be
|
||||
// at the same level as the var
|
||||
cleanFileline(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (m_varp) {
|
||||
nodep->unlinkFrBack();
|
||||
m_varp->addNext(nodep);
|
||||
|
|
@ -296,13 +292,13 @@ private:
|
|||
// AstVar, AstTypedef, AstNodeFTask are common containers
|
||||
AstNode* backp = nodep->backp();
|
||||
for (; backp; backp=backp->backp()) {
|
||||
if (backp->castVar()) break;
|
||||
else if (backp->castTypedef()) break;
|
||||
else if (backp->castNodeFTask()) break;
|
||||
if (VN_IS(backp, Var)) break;
|
||||
else if (VN_IS(backp, Typedef)) break;
|
||||
else if (VN_IS(backp, NodeFTask)) break;
|
||||
}
|
||||
if (!backp) nodep->v3fatalSrc("Implicit enum/struct type created under unexpected node type");
|
||||
AstNodeDType* dtypep = nodep->childDTypep(); dtypep->unlinkFrBack();
|
||||
if (backp->castTypedef()) { // A typedef doesn't need us to make yet another level of typedefing
|
||||
if (VN_IS(backp, Typedef)) { // A typedef doesn't need us to make yet another level of typedefing
|
||||
// For typedefs just remove the AstRefDType level of abstraction
|
||||
nodep->replaceWith(dtypep);
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
|
|
@ -383,7 +379,7 @@ private:
|
|||
//
|
||||
m_modp = nodep;
|
||||
m_valueModp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
m_valueModp = NULL;
|
||||
}
|
||||
|
|
@ -393,7 +389,7 @@ private:
|
|||
//
|
||||
AstNodeModule* upperValueModp = m_valueModp;
|
||||
m_valueModp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_valueModp = upperValueModp;
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) {
|
||||
|
|
@ -414,7 +410,7 @@ private:
|
|||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
cleanFileline(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -428,7 +424,7 @@ public:
|
|||
m_inGenerate = false;
|
||||
m_needStart = false;
|
||||
m_valueModp = NULL;
|
||||
rootp->accept(*this);
|
||||
iterate(rootp);
|
||||
}
|
||||
virtual ~LinkParseVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -60,11 +60,7 @@ private:
|
|||
int m_senitemCvtNum; // Temporary signal counter
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITs
|
||||
// TODO: Most of these visitors are here for historical reasons.
|
||||
|
|
@ -76,11 +72,11 @@ private:
|
|||
if (nodep->dead()) return;
|
||||
m_modp = nodep;
|
||||
m_senitemCvtNum = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Initial assignments under function/tasks can just be simple assignments without the initial
|
||||
if (m_ftaskp) {
|
||||
nodep->replaceWith(nodep->bodysp()->unlinkFrBackWithNext()); VL_DANGLING(nodep);
|
||||
|
|
@ -89,12 +85,12 @@ private:
|
|||
virtual void visit(AstVAssert* nodep) {
|
||||
if (m_assertp) nodep->v3error("Assert not allowed under another assert");
|
||||
m_assertp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_assertp = NULL;
|
||||
}
|
||||
|
||||
virtual void visit(AstVar* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (m_ftaskp) nodep->funcLocal(true);
|
||||
if (nodep->isSigModPublic()) {
|
||||
nodep->sigModPublic(false); // We're done with this attribute
|
||||
|
|
@ -107,21 +103,21 @@ private:
|
|||
if (nodep->varp()) {
|
||||
nodep->varp()->usedParam(true);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeFTask* nodep) {
|
||||
// NodeTask: Remember its name for later resolution
|
||||
// Remember the existing symbol table scope
|
||||
m_ftaskp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_ftaskp = NULL;
|
||||
if (nodep->dpiExport()) {
|
||||
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) {
|
||||
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
|
||||
}
|
||||
|
|
@ -129,15 +125,15 @@ private:
|
|||
|
||||
virtual void visit(AstSenItem* nodep) {
|
||||
// Remove bit selects, and bark if it's not a simple variable
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->isClocked()) {
|
||||
// If it's not a simple variable wrap in a temporary
|
||||
// This is a bit unfortunate as we haven't done width resolution
|
||||
// and any width errors will look a bit odd, but it works.
|
||||
AstNode* sensp = nodep->sensp();
|
||||
if (sensp
|
||||
&& !sensp->castNodeVarRef()
|
||||
&& !sensp->castConst()) {
|
||||
&& !VN_IS(sensp, NodeVarRef)
|
||||
&& !VN_IS(sensp, Const)) {
|
||||
// Make a new temp wire
|
||||
string newvarname = "__Vsenitemexpr"+cvtToStr(++m_senitemCvtNum);
|
||||
AstVar* newvarp = new AstVar (sensp->fileline(), AstVarType::MODULETEMP, newvarname,
|
||||
|
|
@ -145,11 +141,11 @@ private:
|
|||
// We can't just add under the module, because we may be inside a generate, begin, etc.
|
||||
// We know a SenItem should be under a SenTree/Always etc, we we'll just hunt upwards
|
||||
AstNode* addwherep = nodep; // Add to this element's next
|
||||
while (addwherep->castSenItem()
|
||||
|| addwherep->castSenTree()) {
|
||||
while (VN_IS(addwherep, SenItem)
|
||||
|| VN_IS(addwherep, SenTree)) {
|
||||
addwherep = addwherep->backp();
|
||||
}
|
||||
if (!addwherep->castAlways()) { // Assertion perhaps?
|
||||
if (!VN_IS(addwherep, Always)) { // Assertion perhaps?
|
||||
sensp->v3error("Unsupported: Non-single-bit pos/negedge clock statement under some complicated block");
|
||||
addwherep = m_modp;
|
||||
}
|
||||
|
|
@ -166,26 +162,26 @@ private:
|
|||
bool did=1;
|
||||
while (did) {
|
||||
did=0;
|
||||
if (AstNodeSel* selp = nodep->sensp()->castNodeSel()) {
|
||||
if (AstNodeSel* selp = VN_CAST(nodep->sensp(), NodeSel)) {
|
||||
AstNode* fromp = selp->fromp()->unlinkFrBack();
|
||||
selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp);
|
||||
did=1;
|
||||
}
|
||||
// NodeSel doesn't include AstSel....
|
||||
if (AstSel* selp = nodep->sensp()->castSel()) {
|
||||
if (AstSel* selp = VN_CAST(nodep->sensp(), Sel)) {
|
||||
AstNode* fromp = selp->fromp()->unlinkFrBack();
|
||||
selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp);
|
||||
did=1;
|
||||
}
|
||||
if (AstNodePreSel* selp = nodep->sensp()->castNodePreSel()) {
|
||||
if (AstNodePreSel* selp = VN_CAST(nodep->sensp(), NodePreSel)) {
|
||||
AstNode* fromp = selp->lhsp()->unlinkFrBack();
|
||||
selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp);
|
||||
did=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!nodep->sensp()->castNodeVarRef()
|
||||
&& !nodep->sensp()->castEnumItemRef() // V3Const will cleanup
|
||||
if (!VN_IS(nodep->sensp(), NodeVarRef)
|
||||
&& !VN_IS(nodep->sensp(), EnumItemRef) // V3Const will cleanup
|
||||
&& !nodep->isIllegal()) {
|
||||
if (debug()) nodep->dumpTree(cout,"-tree: ");
|
||||
nodep->v3error("Unsupported: Complex statement in sensitivity list");
|
||||
|
|
@ -197,22 +193,22 @@ private:
|
|||
|
||||
virtual void visit(AstNodePreSel* nodep) {
|
||||
if (!nodep->attrp()) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Constification may change the fromp() to a constant, which will lose the
|
||||
// variable we're extracting from (to determine MSB/LSB/endianness/etc.)
|
||||
// So we replicate it in another node
|
||||
// Note that V3Param knows not to replace AstVarRef's under AstAttrOf's
|
||||
AstNode* basefromp = AstArraySel::baseFromp(nodep);
|
||||
if (AstNodeVarRef* varrefp = basefromp->castNodeVarRef()) { // Maybe varxref - so need to clone
|
||||
if (AstNodeVarRef* varrefp = VN_CAST(basefromp, NodeVarRef)) { // Maybe varxref - so need to clone
|
||||
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE,
|
||||
varrefp->cloneTree(false)));
|
||||
} else if (AstUnlinkedRef* uvxrp = basefromp->castUnlinkedRef()) { // Maybe unlinked - so need to clone
|
||||
} else if (AstUnlinkedRef* uvxrp = VN_CAST(basefromp, UnlinkedRef)) { // Maybe unlinked - so need to clone
|
||||
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE,
|
||||
uvxrp->cloneTree(false)));
|
||||
} else if (AstMemberSel* fromp = basefromp->castMemberSel()) {
|
||||
} else if (AstMemberSel* fromp = VN_CAST(basefromp, MemberSel)) {
|
||||
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::MEMBER_BASE,
|
||||
fromp->cloneTree(false)));
|
||||
} else if (AstEnumItemRef* fromp = basefromp->castEnumItemRef()) {
|
||||
} else if (AstEnumItemRef* fromp = VN_CAST(basefromp, EnumItemRef)) {
|
||||
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::ENUM_BASE,
|
||||
fromp->cloneTree(false)));
|
||||
} else {
|
||||
|
|
@ -225,7 +221,7 @@ private:
|
|||
virtual void visit(AstCaseItem* nodep) {
|
||||
// Move default caseItems to the bottom of the list
|
||||
// That saves us from having to search each case list twice, for non-defaults and defaults
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) {
|
||||
nodep->user2(true);
|
||||
AstNode* nextp = nodep->nextp();
|
||||
|
|
@ -252,7 +248,7 @@ private:
|
|||
}
|
||||
}
|
||||
else {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,7 +303,7 @@ private:
|
|||
skipCount--;
|
||||
continue;
|
||||
}
|
||||
AstConst *constp = argp->castConst();
|
||||
AstConst *constp = VN_CAST(argp, Const);
|
||||
bool isFromString = (constp) ? constp->num().isFromString() : false;
|
||||
if (isFromString) {
|
||||
int numchars = argp->dtypep()->width()/8;
|
||||
|
|
@ -356,33 +352,33 @@ private:
|
|||
}
|
||||
|
||||
virtual void visit(AstFOpen* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
|
||||
iterateChildren(nodep);
|
||||
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
||||
}
|
||||
virtual void visit(AstFClose* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
|
||||
iterateChildren(nodep);
|
||||
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
||||
}
|
||||
virtual void visit(AstFEof* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
|
||||
iterateChildren(nodep);
|
||||
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
||||
}
|
||||
virtual void visit(AstFScanF* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);
|
||||
}
|
||||
virtual void visit(AstSScanF* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);
|
||||
}
|
||||
virtual void visit(AstSFormatF* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
// Cleanup old-school displays without format arguments
|
||||
if (!nodep->hasFormat()) {
|
||||
if (nodep->text()!="") nodep->v3fatalSrc("Non-format $sformatf should have \"\" format");
|
||||
if (nodep->exprsp()->castConst()
|
||||
&& nodep->exprsp()->castConst()->num().isFromString()) {
|
||||
AstConst* fmtp = nodep->exprsp()->unlinkFrBack()->castConst();
|
||||
if (VN_IS(nodep->exprsp(), Const)
|
||||
&& VN_CAST(nodep->exprsp(), Const)->num().isFromString()) {
|
||||
AstConst* fmtp = VN_CAST(nodep->exprsp()->unlinkFrBack(), Const);
|
||||
nodep->text(fmtp->num().toString());
|
||||
pushDeletep(fmtp); VL_DANGLING(fmtp);
|
||||
}
|
||||
|
|
@ -390,13 +386,14 @@ private:
|
|||
}
|
||||
string newFormat = expectFormat(nodep, nodep->text(), nodep->exprsp(), false);
|
||||
nodep->text(newFormat);
|
||||
if ((nodep->backp()->castDisplay() && nodep->backp()->castDisplay()->displayType().needScopeTracking())
|
||||
if ((VN_IS(nodep->backp(), Display)
|
||||
&& VN_CAST(nodep->backp(), Display)->displayType().needScopeTracking())
|
||||
|| nodep->formatScopeTracking()) {
|
||||
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
|
||||
}
|
||||
}
|
||||
virtual void visit(AstDisplay* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstUdpTable* nodep) {
|
||||
|
|
@ -408,7 +405,7 @@ private:
|
|||
// Massive hack, just tie off all outputs so our analysis can proceed
|
||||
AstVar* varoutp = NULL;
|
||||
for (AstNode* stmtp = m_modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
|
||||
if (AstVar* varp = stmtp->castVar()) {
|
||||
if (AstVar* varp = VN_CAST(stmtp, Var)) {
|
||||
if (varp->isInput()) {
|
||||
} else if (varp->isOutput()) {
|
||||
if (varoutp) { varp->v3error("Multiple outputs not allowed in udp modules"); }
|
||||
|
|
@ -429,22 +426,22 @@ private:
|
|||
virtual void visit(AstScCtor* nodep) {
|
||||
// Constructor info means the module must remain public
|
||||
m_modp->modPublic(true);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstScDtor* nodep) {
|
||||
// Destructor info means the module must remain public
|
||||
m_modp->modPublic(true);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstScInt* nodep) {
|
||||
// Special class info means the module must remain public
|
||||
m_modp->modPublic(true);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -455,7 +452,7 @@ public:
|
|||
m_assertp = NULL;
|
||||
m_senitemCvtNum = 0;
|
||||
//
|
||||
rootp->accept(*this);
|
||||
iterate(rootp);
|
||||
}
|
||||
virtual ~LinkResolveVisitor() {}
|
||||
};
|
||||
|
|
@ -471,20 +468,16 @@ private:
|
|||
AstNodeModule* m_modp; // Current module
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// VISITs
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
// Iterate modules backwards, in bottom-up order.
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
iterateChildrenBackwards(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstCell* nodep) {
|
||||
|
|
@ -497,14 +490,14 @@ private:
|
|||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit LinkBotupVisitor(AstNetlist* rootp) {
|
||||
m_modp = NULL;
|
||||
//
|
||||
rootp->accept(*this);
|
||||
iterate(rootp);
|
||||
}
|
||||
virtual ~LinkBotupVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -50,11 +50,7 @@ protected:
|
|||
// AstVar::user4() -> AstVarRef*. First place signal set; must be first assignment
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// TYPES
|
||||
union VarFlags {
|
||||
|
|
@ -90,12 +86,12 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit LocalizeDehierVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~LocalizeDehierVisitor() {}
|
||||
};
|
||||
|
|
@ -114,7 +110,7 @@ private:
|
|||
// STATE
|
||||
V3Double0 m_statLocVars; // Statistic tracking
|
||||
AstCFunc* m_cfuncp; // Current active function
|
||||
vector<AstVar*> m_varps; // List of variables to consider for deletion
|
||||
std::vector<AstVar*> m_varps; // List of variables to consider for deletion
|
||||
|
||||
// METHODS
|
||||
void clearOptimizable(AstVar* nodep, const char* reason) {
|
||||
|
|
@ -130,7 +126,7 @@ private:
|
|||
flags.setNodeFlags(nodep);
|
||||
}
|
||||
void moveVars() {
|
||||
for (vector<AstVar*>::iterator it = m_varps.begin(); it != m_varps.end(); ++it) {
|
||||
for (std::vector<AstVar*>::iterator it = m_varps.begin(); it != m_varps.end(); ++it) {
|
||||
AstVar* nodep = *it;
|
||||
if (nodep->valuep()) clearOptimizable(nodep,"HasInitValue");
|
||||
if (!VarFlags(nodep).m_stdFuncAsn) clearStdOptimizable(nodep,"NoStdAssign");
|
||||
|
|
@ -142,7 +138,7 @@ private:
|
|||
// We don't need to test for tracing; it would be in the tracefunc if it was needed
|
||||
UINFO(4," ModVar->BlkVar "<<nodep<<endl);
|
||||
++m_statLocVars;
|
||||
AstCFunc* newfuncp = nodep->user1p()->castCFunc();
|
||||
AstCFunc* newfuncp = VN_CAST(nodep->user1p(), CFunc);
|
||||
nodep->unlinkFrBack();
|
||||
newfuncp->addInitsp(nodep);
|
||||
// Done
|
||||
|
|
@ -157,7 +153,7 @@ private:
|
|||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
moveVars();
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
|
|
@ -167,7 +163,7 @@ private:
|
|||
searchFuncStmts(nodep->initsp());
|
||||
searchFuncStmts(nodep->stmtsp());
|
||||
searchFuncStmts(nodep->finalsp());
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_cfuncp = NULL;
|
||||
}
|
||||
void searchFuncStmts(AstNode* nodep) {
|
||||
|
|
@ -176,8 +172,8 @@ private:
|
|||
// This could be more complicated; allow always-set under both branches of a IF.
|
||||
// If so, check for ArrayRef's and such, as they aren't acceptable.
|
||||
for (; nodep; nodep=nodep->nextp()) {
|
||||
if (nodep->castNodeAssign()) {
|
||||
if (AstVarRef* varrefp = nodep->castNodeAssign()->lhsp()->castVarRef()) {
|
||||
if (VN_IS(nodep, NodeAssign)) {
|
||||
if (AstVarRef* varrefp = VN_CAST(VN_CAST(nodep, NodeAssign)->lhsp(), VarRef)) {
|
||||
if (!varrefp->lvalue()) varrefp->v3fatalSrc("LHS assignment not lvalue");
|
||||
if (!varrefp->varp()->user4p()) {
|
||||
UINFO(4," FuncAsn "<<varrefp<<endl);
|
||||
|
|
@ -230,13 +226,13 @@ private:
|
|||
// No iterate; Don't want varrefs under it
|
||||
}
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit LocalizeVisitor(AstNetlist* nodep) {
|
||||
m_cfuncp = NULL;
|
||||
nodep->accept(*this);
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~LocalizeVisitor() {
|
||||
V3Stats::addStat("Optimizations, Vars localized", m_statLocVars);
|
||||
|
|
|
|||
|
|
@ -52,11 +52,7 @@ private:
|
|||
V3LanguageWords m_words; // Reserved word detector
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void rename(AstNode* nodep, bool addPvt) {
|
||||
if (!nodep->user1()) { // Not already done
|
||||
|
|
@ -78,7 +74,7 @@ private:
|
|||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) {
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
// Add __PVT__ to names of local signals
|
||||
|
|
@ -91,54 +87,55 @@ private:
|
|||
}
|
||||
virtual void visit(AstCFunc* nodep) {
|
||||
if (!nodep->user1()) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
rename(nodep, false);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
if (nodep->varp()) {
|
||||
nodep->varp()->iterate(*this);
|
||||
iterate(nodep->varp());
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep) {
|
||||
if (!nodep->user1()) {
|
||||
rename(nodep, !nodep->modp()->modPublic());
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstMemberDType* nodep) {
|
||||
if (!nodep->user1()) {
|
||||
rename(nodep, false);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstMemberSel* nodep) {
|
||||
if (!nodep->user1()) {
|
||||
rename(nodep, false);
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScope* nodep) {
|
||||
if (!nodep->user1SetOnce()) {
|
||||
if (nodep->aboveScopep()) nodep->aboveScopep()->iterate(*this);
|
||||
if (nodep->aboveCellp()) nodep->aboveCellp()->iterate(*this);
|
||||
if (nodep->aboveScopep()) iterate(nodep->aboveScopep());
|
||||
if (nodep->aboveCellp()) iterate(nodep->aboveCellp());
|
||||
// Always recompute name (as many level above scope may have changed)
|
||||
// Same formula as V3Scope
|
||||
nodep->name(nodep->isTop() ? "TOP"
|
||||
: (nodep->aboveScopep()->name()+"."+nodep->aboveCellp()->name()));
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) {
|
||||
nodep->iterateChildren(*this);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit NameVisitor(AstNetlist* nodep) {
|
||||
nodep->accept(*this);
|
||||
m_modp = NULL;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~NameVisitor() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ V3Number& V3Number::setMask(int nbits) {
|
|||
// ACCESSORS - as strings
|
||||
|
||||
string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
|
||||
ostringstream out;
|
||||
std::ostringstream out;
|
||||
|
||||
if (isDouble()) {
|
||||
out.precision(17);
|
||||
|
|
@ -781,7 +781,7 @@ bool V3Number::isUnknown() const {
|
|||
return false;
|
||||
}
|
||||
bool V3Number::isLt(const V3Number& rhs) const {
|
||||
for (int bit=0; bit<max(this->width(),rhs.width()); bit++) {
|
||||
for (int bit=0; bit < std::max(this->width(),rhs.width()); bit++) {
|
||||
if (this->bitIs1(bit) && rhs.bitIs0(bit)) { return 1; }
|
||||
if (rhs.bitIs1(bit) && this->bitIs0(bit)) { return 0; }
|
||||
if (this->bitIsXZ(bit)) { return 0; }
|
||||
|
|
@ -791,7 +791,7 @@ bool V3Number::isLt(const V3Number& rhs) const {
|
|||
}
|
||||
bool V3Number::isLtXZ(const V3Number& rhs) const {
|
||||
// Include X/Z in comparisons for sort ordering
|
||||
for (int bit=0; bit<max(this->width(),rhs.width()); bit++) {
|
||||
for (int bit=0; bit < std::max(this->width(),rhs.width()); bit++) {
|
||||
if (this->bitIs1(bit) && rhs.bitIs0(bit)) { return 1; }
|
||||
if (rhs.bitIs1(bit) && this->bitIs0(bit)) { return 0; }
|
||||
if (this->bitIsXZ(bit)) { return 1; }
|
||||
|
|
@ -1068,9 +1068,9 @@ V3Number& V3Number::opStreamL (const V3Number& lhs, const V3Number& rhs) {
|
|||
m_fileline->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in streams.");
|
||||
}
|
||||
// Slice size should never exceed the lhs width
|
||||
int ssize=min(rhs.toUInt(), (unsigned)lhs.width());
|
||||
int ssize = std::min(rhs.toUInt(), (unsigned)lhs.width());
|
||||
for (int istart=0; istart<lhs.width(); istart+=ssize) {
|
||||
int ostart=max(0, lhs.width()-ssize-istart);
|
||||
int ostart = std::max(0, lhs.width()-ssize-istart);
|
||||
for (int bit=0; bit<ssize && bit<lhs.width()-istart; bit++) {
|
||||
setBit(ostart+bit, lhs.bitIs(istart+bit));
|
||||
}
|
||||
|
|
@ -1126,7 +1126,7 @@ V3Number& V3Number::opLogIff (const V3Number& lhs, const V3Number& rhs) {
|
|||
V3Number& V3Number::opEq (const V3Number& lhs, const V3Number& rhs) {
|
||||
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
|
||||
char outc = 1;
|
||||
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
|
||||
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
|
||||
if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { outc=0; goto last; }
|
||||
if (lhs.bitIs0(bit) && rhs.bitIs1(bit)) { outc=0; goto last; }
|
||||
if (lhs.bitIsXZ(bit)) { outc='x'; }
|
||||
|
|
@ -1139,7 +1139,7 @@ last:
|
|||
V3Number& V3Number::opNeq (const V3Number& lhs, const V3Number& rhs) {
|
||||
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
|
||||
char outc = 0;
|
||||
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
|
||||
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
|
||||
if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { outc=1; goto last; }
|
||||
if (lhs.bitIs0(bit) && rhs.bitIs1(bit)) { outc=1; goto last; }
|
||||
if (lhs.bitIsXZ(bit)) { outc='x'; }
|
||||
|
|
@ -1152,7 +1152,7 @@ last:
|
|||
bool V3Number::isCaseEq (const V3Number& rhs) const {
|
||||
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
|
||||
if (this->width() != rhs.width()) return false;
|
||||
for (int bit=0; bit<max(this->width(),rhs.width()); bit++) {
|
||||
for (int bit=0; bit < std::max(this->width(),rhs.width()); bit++) {
|
||||
if (this->bitIs(bit) != rhs.bitIs(bit)) { return false; }
|
||||
}
|
||||
return true;
|
||||
|
|
@ -1165,7 +1165,7 @@ V3Number& V3Number::opCaseEq (const V3Number& lhs, const V3Number& rhs) {
|
|||
V3Number& V3Number::opCaseNeq (const V3Number& lhs, const V3Number& rhs) {
|
||||
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
|
||||
char outc = 0;
|
||||
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
|
||||
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
|
||||
if (lhs.bitIs(bit) != rhs.bitIs(bit)) { outc=1; goto last; }
|
||||
}
|
||||
last:
|
||||
|
|
@ -1174,7 +1174,7 @@ last:
|
|||
|
||||
V3Number& V3Number::opWildEq (const V3Number& lhs, const V3Number& rhs) {
|
||||
char outc = 1;
|
||||
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
|
||||
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
|
||||
if (!rhs.bitIsXZ(bit)
|
||||
&& lhs.bitIs(bit) != rhs.bitIs(bit)) { outc=0; goto last; }
|
||||
if (lhs.bitIsXZ(bit)) outc='x';
|
||||
|
|
@ -1185,7 +1185,7 @@ last:
|
|||
|
||||
V3Number& V3Number::opWildNeq (const V3Number& lhs, const V3Number& rhs) {
|
||||
char outc = 0;
|
||||
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
|
||||
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
|
||||
if (!rhs.bitIsXZ(bit)
|
||||
&& lhs.bitIs(bit) != rhs.bitIs(bit)) { outc=1; goto last; }
|
||||
if (lhs.bitIsXZ(bit)) outc='x';
|
||||
|
|
@ -1197,7 +1197,7 @@ last:
|
|||
V3Number& V3Number::opGt (const V3Number& lhs, const V3Number& rhs) {
|
||||
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
|
||||
char outc = 0;
|
||||
for (int bit=0; bit<max(lhs.width(),rhs.width()); bit++) {
|
||||
for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) {
|
||||
if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { outc=1; }
|
||||
if (rhs.bitIs1(bit) && lhs.bitIs0(bit)) { outc=0; }
|
||||
if (lhs.bitIsXZ(bit)) { outc='x'; }
|
||||
|
|
@ -1210,14 +1210,14 @@ V3Number& V3Number::opGtS (const V3Number& lhs, const V3Number& rhs) {
|
|||
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
|
||||
char outc = 0;
|
||||
{
|
||||
int mbit=max(lhs.width()-1,rhs.width()-1);
|
||||
int mbit = std::max(lhs.width()-1,rhs.width()-1);
|
||||
if (lhs.bitIsXZ(mbit)) { outc='x'; }
|
||||
else if (rhs.bitIsXZ(mbit)) { outc='x'; }
|
||||
else if (lhs.bitIs0(mbit) && rhs.bitIs1Extend(mbit)) { outc=1; } // + > -
|
||||
else if (lhs.bitIs1Extend(mbit) && rhs.bitIs0(mbit)) { outc=0; } // - !> +
|
||||
else {
|
||||
// both positive or negative, normal >
|
||||
for (int bit=0; bit<max(lhs.width()-1,rhs.width()-1); bit++) {
|
||||
for (int bit=0; bit < std::max(lhs.width()-1,rhs.width()-1); bit++) {
|
||||
if (lhs.bitIs1Extend(bit) && rhs.bitIs0(bit)) { outc=1; }
|
||||
if (rhs.bitIs1Extend(bit) && lhs.bitIs0(bit)) { outc=0; }
|
||||
if (lhs.bitIsXZ(bit)) { outc='x'; }
|
||||
|
|
@ -1497,7 +1497,7 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
|
|||
m_value[j] = unw64 / (vluint64_t)(rhs.m_value[0]);
|
||||
k = unw64 - (vluint64_t)(m_value[j])*(vluint64_t)(rhs.m_value[0]);
|
||||
}
|
||||
UINFO(9, " opmoddiv-1w "<<lhs<<" "<<rhs<<" q="<<*this<<" rem=0x"<<hex<<k<<dec<<endl);
|
||||
UINFO(9, " opmoddiv-1w "<<lhs<<" "<<rhs<<" q="<<*this<<" rem=0x"<<std::hex<<k<<std::dec<<endl);
|
||||
if (is_modulus) { setZero(); m_value[0] = k; }
|
||||
opCleanThis();
|
||||
return *this;
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ class V3Number {
|
|||
bool m_fromString:1; // True if from string literal
|
||||
bool m_autoExtend:1; // True if SystemVerilog extend-to-any-width
|
||||
FileLine* m_fileline;
|
||||
vector<uint32_t> m_value; // The Value, with bit 0 being in bit 0 of this vector (unless X/Z)
|
||||
vector<uint32_t> m_valueX; // Each bit is true if it's X or Z, 10=z, 11=x
|
||||
std::vector<uint32_t> m_value; // The Value, with bit 0 being in bit 0 of this vector (unless X/Z)
|
||||
std::vector<uint32_t> m_valueX; // Each bit is true if it's X or Z, 10=z, 11=x
|
||||
string m_stringVal; // If isString, the value of the string
|
||||
// METHODS
|
||||
V3Number& setSingleBits(char value);
|
||||
|
|
@ -330,6 +330,6 @@ public:
|
|||
V3Number& opLtN (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opLteN (const V3Number& lhs, const V3Number& rhs);
|
||||
};
|
||||
inline ostream& operator<<(ostream& os, const V3Number& rhs) { return os<<rhs.ascii(); }
|
||||
inline std::ostream& operator<<(std::ostream& os, const V3Number& rhs) { return os<<rhs.ascii(); }
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -50,17 +50,17 @@
|
|||
class V3OptionsImp {
|
||||
public:
|
||||
// TYPES
|
||||
typedef std::map<string,set<string> > DirMap; // Directory listing
|
||||
typedef std::map<string,std::set<string> > DirMap; // Directory listing
|
||||
|
||||
// STATE
|
||||
list<string> m_allArgs; // List of every argument encountered
|
||||
list<string> m_incDirUsers; // Include directories (ordered)
|
||||
set<string> m_incDirUserSet; // Include directories (for removing duplicates)
|
||||
list<string> m_incDirFallbacks; // Include directories (ordered)
|
||||
set<string> m_incDirFallbackSet; // Include directories (for removing duplicates)
|
||||
map<string,V3LangCode> m_langExts; // Language extension map
|
||||
list<string> m_libExtVs; // Library extensions (ordered)
|
||||
set<string> m_libExtVSet; // Library extensions (for removing duplicates)
|
||||
std::list<string> m_allArgs; // List of every argument encountered
|
||||
std::list<string> m_incDirUsers; // Include directories (ordered)
|
||||
std::set<string> m_incDirUserSet; // Include directories (for removing duplicates)
|
||||
std::list<string> m_incDirFallbacks; // Include directories (ordered)
|
||||
std::set<string> m_incDirFallbackSet; // Include directories (for removing duplicates)
|
||||
std::map<string,V3LangCode> m_langExts; // Language extension map
|
||||
std::list<string> m_libExtVs; // Library extensions (ordered)
|
||||
std::set<string> m_libExtVSet; // Library extensions (for removing duplicates)
|
||||
DirMap m_dirMap; // Directory listing
|
||||
|
||||
// ACCESSOR METHODS
|
||||
|
|
@ -167,9 +167,9 @@ string V3Options::parameter(const string& name) {
|
|||
|
||||
void V3Options::checkParameters() {
|
||||
if (!m_parameters.empty()) {
|
||||
stringstream msg;
|
||||
std::stringstream msg;
|
||||
msg << "Parameters from the command line were not found in the design:";
|
||||
for (map<string,string>::iterator it = m_parameters.begin();
|
||||
for (std::map<string,string>::iterator it = m_parameters.begin();
|
||||
it != m_parameters.end(); ++it) {
|
||||
msg << " " << it->first;
|
||||
}
|
||||
|
|
@ -235,7 +235,7 @@ void V3Options::addArg(const string& arg) {
|
|||
|
||||
string V3Options::allArgsString() {
|
||||
string out;
|
||||
for (list<string>::iterator it=m_impp->m_allArgs.begin(); it!=m_impp->m_allArgs.end(); ++it) {
|
||||
for (std::list<string>::iterator it=m_impp->m_allArgs.begin(); it!=m_impp->m_allArgs.end(); ++it) {
|
||||
if (out != "") out += " ";
|
||||
out += *it;
|
||||
}
|
||||
|
|
@ -297,10 +297,10 @@ string V3Options::fileExists (const string& filename) {
|
|||
V3OptionsImp::DirMap::iterator diriter = m_impp->m_dirMap.find(dir);
|
||||
if (diriter == m_impp->m_dirMap.end()) {
|
||||
// Read the listing
|
||||
m_impp->m_dirMap.insert(make_pair(dir, set<string>() ));
|
||||
m_impp->m_dirMap.insert(std::make_pair(dir, std::set<string>() ));
|
||||
diriter = m_impp->m_dirMap.find(dir);
|
||||
|
||||
set<string>* setp = &(diriter->second);
|
||||
std::set<string>* setp = &(diriter->second);
|
||||
|
||||
if (DIR* dirp = opendir(dir.c_str())) {
|
||||
while (struct dirent* direntp = readdir(dirp)) {
|
||||
|
|
@ -311,8 +311,8 @@ string V3Options::fileExists (const string& filename) {
|
|||
}
|
||||
}
|
||||
// Find it
|
||||
set<string>* filesetp = &(diriter->second);
|
||||
set<string>::iterator fileiter = filesetp->find(basename);
|
||||
std::set<string>* filesetp = &(diriter->second);
|
||||
std::set<string>::iterator fileiter = filesetp->find(basename);
|
||||
if (fileiter == filesetp->end()) {
|
||||
return ""; // Not found
|
||||
}
|
||||
|
|
@ -323,7 +323,7 @@ string V3Options::fileExists (const string& filename) {
|
|||
}
|
||||
|
||||
string V3Options::filePathCheckOneDir(const string& modname, const string& dirname) {
|
||||
for (list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
|
||||
for (std::list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
|
||||
string fn = V3Os::filenameFromDirBase(dirname, modname+*extIter);
|
||||
string exists = fileExists(fn);
|
||||
if (exists!="") {
|
||||
|
|
@ -340,12 +340,12 @@ string V3Options::filePath (FileLine* fl, const string& modname, const string& l
|
|||
// Find a filename to read the specified module name,
|
||||
// using the incdir and libext's.
|
||||
// Return "" if not found.
|
||||
for (list<string>::iterator dirIter=m_impp->m_incDirUsers.begin();
|
||||
for (std::list<string>::iterator dirIter=m_impp->m_incDirUsers.begin();
|
||||
dirIter!=m_impp->m_incDirUsers.end(); ++dirIter) {
|
||||
string exists = filePathCheckOneDir(modname, *dirIter);
|
||||
if (exists!="") return exists;
|
||||
}
|
||||
for (list<string>::iterator dirIter=m_impp->m_incDirFallbacks.begin();
|
||||
for (std::list<string>::iterator dirIter=m_impp->m_incDirFallbacks.begin();
|
||||
dirIter!=m_impp->m_incDirFallbacks.end(); ++dirIter) {
|
||||
string exists = filePathCheckOneDir(modname, *dirIter);
|
||||
if (exists!="") return exists;
|
||||
|
|
@ -372,16 +372,16 @@ void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) {
|
|||
fl->v3error("This may be because there's no search path specified with -I<dir>."<<endl);
|
||||
}
|
||||
fl->v3error("Looked in:"<<endl);
|
||||
for (list<string>::iterator dirIter=m_impp->m_incDirUsers.begin();
|
||||
for (std::list<string>::iterator dirIter=m_impp->m_incDirUsers.begin();
|
||||
dirIter!=m_impp->m_incDirUsers.end(); ++dirIter) {
|
||||
for (list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
|
||||
for (std::list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
|
||||
string fn = V3Os::filenameFromDirBase(*dirIter,modname+*extIter);
|
||||
fl->v3error(" "<<fn<<endl);
|
||||
}
|
||||
}
|
||||
for (list<string>::iterator dirIter=m_impp->m_incDirFallbacks.begin();
|
||||
for (std::list<string>::iterator dirIter=m_impp->m_incDirFallbacks.begin();
|
||||
dirIter!=m_impp->m_incDirFallbacks.end(); ++dirIter) {
|
||||
for (list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
|
||||
for (std::list<string>::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) {
|
||||
string fn = V3Os::filenameFromDirBase(*dirIter,modname+*extIter);
|
||||
fl->v3error(" "<<fn<<endl);
|
||||
}
|
||||
|
|
@ -398,7 +398,7 @@ V3LangCode V3Options::fileLanguage(const string &filename) {
|
|||
string::size_type pos;
|
||||
if ((pos = ext.rfind(".")) != string::npos) {
|
||||
ext.erase(0, pos + 1);
|
||||
map<string,V3LangCode>::iterator it = m_impp->m_langExts.find(ext);
|
||||
std::map<string,V3LangCode>::iterator it = m_impp->m_langExts.find(ext);
|
||||
if (it != m_impp->m_langExts.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
|
@ -661,6 +661,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
else if ( !strcmp (sw, "-debug-abort") ) { abort(); } // Undocumented, see also --debug-sigsegv
|
||||
else if ( onoff (sw, "-debug-check", flag/*ref*/) ){ m_debugCheck = flag; }
|
||||
else if ( onoff (sw, "-debug-leak", flag/*ref*/) ){ m_debugLeak = flag; }
|
||||
else if ( onoff (sw, "-debug-nondeterminism", flag/*ref*/) ){ m_debugNondeterminism = flag; }
|
||||
else if ( onoff (sw, "-debug-partition", flag/*ref*/) ){ m_debugPartition = flag; } // Undocumented
|
||||
else if ( onoff (sw, "-debug-self-test", flag/*ref*/) ){ m_debugSelfTest = flag; } // Undocumented
|
||||
else if ( !strcmp (sw, "-debug-sigsegv") ) { throwSigsegv(); } // Undocumented, see also --debug-abort
|
||||
else if ( !strcmp (sw, "-debug-fatalsrc") ) { v3fatalSrc("--debug-fatal-src"); } // Undocumented, see also --debug-abort
|
||||
else if ( onoff (sw, "-decoration", flag/*ref*/) ) { m_decoration = flag; }
|
||||
|
|
@ -678,6 +681,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
else if ( !strcmp (sw, "-private") ) { m_public = false; }
|
||||
else if ( onoff (sw, "-prof-cfuncs", flag/*ref*/) ) { m_profCFuncs = flag; }
|
||||
else if ( onoff (sw, "-profile-cfuncs", flag/*ref*/) ) { m_profCFuncs = flag; } // Undocumented, for backward compat
|
||||
else if ( onoff (sw, "-prof-threads", flag/*ref*/) ) { m_profThreads = flag; }
|
||||
else if ( onoff (sw, "-public", flag/*ref*/) ) { m_public = flag; }
|
||||
else if ( !strncmp(sw, "-pvalue+", strlen("-pvalue+"))) { addParameter(string(sw+strlen("-pvalue+")), false); }
|
||||
else if ( onoff (sw, "-relative-cfuncs", flag/*ref*/) ) { m_relativeCFuncs = flag; }
|
||||
|
|
@ -689,6 +693,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
else if ( onoff (sw, "-stats", flag/*ref*/) ) { m_stats = flag; }
|
||||
else if ( onoff (sw, "-stats-vars", flag/*ref*/) ) { m_statsVars = flag; m_stats |= flag; }
|
||||
else if ( !strcmp (sw, "-sv") ) { m_defaultLanguage = V3LangCode::L1800_2005; }
|
||||
else if ( onoff (sw, "-threads-coarsen", flag/*ref*/)) { m_threadsCoarsen = flag; } // Undocumented, debug
|
||||
else if ( onoff (sw, "-trace", flag/*ref*/) ) { m_trace = flag; }
|
||||
else if ( onoff (sw, "-trace-dups", flag/*ref*/) ) { m_traceDups = flag; }
|
||||
else if ( onoff (sw, "-trace-params", flag/*ref*/) ) { m_traceParams = flag; }
|
||||
|
|
@ -723,6 +728,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
case 's': m_oSplit = flag; break;
|
||||
case 't': m_oLifePost = flag; break;
|
||||
case 'u': m_oSubst = flag; break;
|
||||
case 'v': m_oReloop = flag; break;
|
||||
case 'x': m_oExpand = flag; break;
|
||||
case 'y': m_oAcycSimp = flag; break;
|
||||
case 'z': m_oLocalize = flag; break;
|
||||
|
|
@ -1012,6 +1018,20 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
|||
shift; m_threads = atoi(argv[i]);
|
||||
if (m_threads < 0) fl->v3fatal("--threads must be >= 0: "<<argv[i]);
|
||||
}
|
||||
else if ( !strcmp (sw, "-threads-dpi") && (i+1)<argc) {
|
||||
shift;
|
||||
if (!strcmp(argv[i], "all")) { m_threadsDpiPure=true; m_threadsDpiUnpure=true; }
|
||||
else if (!strcmp(argv[i], "none")) { m_threadsDpiPure=false; m_threadsDpiUnpure=false; }
|
||||
else if (!strcmp(argv[i], "pure")) { m_threadsDpiPure=true; m_threadsDpiUnpure=false; }
|
||||
else {
|
||||
fl->v3fatal("Unknown setting for --threads-dpi: "<<argv[i]);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp (sw, "-threads-max-mtasks") ) {
|
||||
shift; m_threadsMaxMTasks = atoi(argv[i]);
|
||||
if (m_threadsMaxMTasks < 1)
|
||||
fl->v3fatal("--threads-max-mtasks must be >= 1: "<<argv[i]);
|
||||
}
|
||||
else if ( !strcmp (sw, "-top-module") && (i+1)<argc ) {
|
||||
shift; m_topModule = argv[i];
|
||||
}
|
||||
|
|
@ -1075,7 +1095,7 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) {
|
|||
// Read the specified -f filename and process as arguments
|
||||
UINFO(1,"Reading Options File "<<filename<<endl);
|
||||
|
||||
const vl_unique_ptr<ifstream> ifp (V3File::new_ifstream(filename));
|
||||
const vl_unique_ptr<std::ifstream> ifp (V3File::new_ifstream(filename));
|
||||
if (ifp->fail()) {
|
||||
fl->v3error("Cannot open -f command file: "+filename);
|
||||
return;
|
||||
|
|
@ -1120,7 +1140,7 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) {
|
|||
}
|
||||
|
||||
// Strip off arguments and parse into words
|
||||
vector<string> args;
|
||||
std::vector<string> args;
|
||||
string::size_type startpos = 0;
|
||||
while (startpos < whole_file.length()) {
|
||||
while (isspace(whole_file[startpos])) ++startpos;
|
||||
|
|
@ -1222,6 +1242,9 @@ V3Options::V3Options() {
|
|||
m_coverageUser = false;
|
||||
m_debugCheck = false;
|
||||
m_debugLeak = true;
|
||||
m_debugNondeterminism = false;
|
||||
m_debugPartition = false;
|
||||
m_debugSelfTest = false;
|
||||
m_decoration = true;
|
||||
m_exe = false;
|
||||
m_ignc = false;
|
||||
|
|
@ -1236,6 +1259,7 @@ V3Options::V3Options() {
|
|||
m_pinsScBigUint = false;
|
||||
m_pinsUint8 = false;
|
||||
m_profCFuncs = false;
|
||||
m_profThreads = false;
|
||||
m_preprocOnly = false;
|
||||
m_preprocNoLine = false;
|
||||
m_public = false;
|
||||
|
|
@ -1248,6 +1272,10 @@ V3Options::V3Options() {
|
|||
m_statsVars = false;
|
||||
m_systemC = false;
|
||||
m_threads = 0;
|
||||
m_threadsDpiPure = true;
|
||||
m_threadsDpiUnpure = false;
|
||||
m_threadsCoarsen = true;
|
||||
m_threadsMaxMTasks = 0;
|
||||
m_trace = false;
|
||||
m_traceDups = false;
|
||||
m_traceParams = true;
|
||||
|
|
@ -1365,6 +1393,7 @@ void V3Options::optimize(int level) {
|
|||
m_oLife = flag;
|
||||
m_oLifePost = flag;
|
||||
m_oLocalize = flag;
|
||||
m_oReloop = flag;
|
||||
m_oReorder = flag;
|
||||
m_oSplit = flag;
|
||||
m_oSubst = flag;
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@
|
|||
class V3OptionsImp;
|
||||
class FileLine;
|
||||
|
||||
typedef vector<string> V3StringList;
|
||||
typedef set<string> V3StringSet;
|
||||
typedef std::vector<string> V3StringList;
|
||||
typedef std::set<string> V3StringSet;
|
||||
|
||||
class V3Options {
|
||||
// TYPES
|
||||
typedef map<string,int> DebugSrcMap;
|
||||
typedef std::map<string,int> DebugSrcMap;
|
||||
|
||||
// MEMBERS (general options)
|
||||
V3OptionsImp* m_impp; // Slow hidden options
|
||||
|
|
@ -58,7 +58,7 @@ class V3Options {
|
|||
V3StringList m_forceIncs; // argument: -FI
|
||||
DebugSrcMap m_debugSrcs; // argument: --debugi-<srcfile>=<level>
|
||||
DebugSrcMap m_dumpTrees; // argument: --dump-treei-<srcfile>=<level>
|
||||
map<string,string> m_parameters; // Parameters
|
||||
std::map<string,string> m_parameters; // Parameters
|
||||
|
||||
|
||||
bool m_preprocOnly; // main switch: -E
|
||||
|
|
@ -75,7 +75,10 @@ class V3Options {
|
|||
bool m_coverageUnderscore;// main switch: --coverage-underscore
|
||||
bool m_coverageUser; // main switch: --coverage-func
|
||||
bool m_debugCheck; // main switch: --debug-check
|
||||
bool m_debugLeak; // main switch: --debug-leak
|
||||
bool m_debugLeak; // main switch: --debug-leak
|
||||
bool m_debugNondeterminism; // main switch: --debug-nondeterminism
|
||||
bool m_debugPartition; // main switch: --debug-partition
|
||||
bool m_debugSelfTest; // main switch: --debug-self-test
|
||||
bool m_decoration; // main switch: --decoration
|
||||
bool m_exe; // main switch: --exe
|
||||
bool m_ignc; // main switch: --ignc
|
||||
|
|
@ -87,6 +90,7 @@ class V3Options {
|
|||
bool m_pinsScBigUint;// main switch: --pins-sc-biguint
|
||||
bool m_pinsUint8; // main switch: --pins-uint8
|
||||
bool m_profCFuncs; // main switch: --prof-cfuncs
|
||||
bool m_profThreads; // main switch: --prof-threads
|
||||
bool m_public; // main switch: --public
|
||||
bool m_relativeCFuncs; // main switch: --relative-cfuncs
|
||||
bool m_relativeIncludes; // main switch: --relative-includes
|
||||
|
|
@ -96,6 +100,9 @@ class V3Options {
|
|||
bool m_skipIdentical;// main switch: --skip-identical
|
||||
bool m_stats; // main switch: --stats
|
||||
bool m_statsVars; // main switch: --stats-vars
|
||||
bool m_threadsCoarsen; // main switch: --threads-coarsen
|
||||
bool m_threadsDpiPure; // main switch: --threads-dpi all/pure
|
||||
bool m_threadsDpiUnpure; // main switch: --threads-dpi all
|
||||
bool m_trace; // main switch: --trace
|
||||
bool m_traceDups; // main switch: --trace-dups
|
||||
bool m_traceParams; // main switch: --trace-params
|
||||
|
|
@ -117,6 +124,7 @@ class V3Options {
|
|||
int m_outputSplitCTrace;// main switch: --output-split-ctrace
|
||||
int m_pinsBv; // main switch: --pins-bv
|
||||
int m_threads; // main switch: --threads (0 == --no-threads)
|
||||
int m_threadsMaxMTasks; // main switch: --threads-max-mtasks
|
||||
int m_traceDepth; // main switch: --trace-depth
|
||||
int m_traceMaxArray;// main switch: --trace-max-array
|
||||
int m_traceMaxWidth;// main switch: --trace-max-width
|
||||
|
|
@ -159,6 +167,7 @@ class V3Options {
|
|||
bool m_oLifePost; // main switch: -Ot: delayed assignment elimination
|
||||
bool m_oLocalize; // main switch: -Oz: convert temps to local variables
|
||||
bool m_oInline; // main switch: -Oi: module inlining
|
||||
bool m_oReloop; // main switch: -Ov: reform loops
|
||||
bool m_oReorder; // main switch: -Or: reorder assignments in blocks
|
||||
bool m_oSplit; // main switch: -Os: always assignment splitting
|
||||
bool m_oSubst; // main switch: -Ou: substitute expression temp values
|
||||
|
|
@ -231,8 +240,14 @@ class V3Options {
|
|||
bool coverageUser() const { return m_coverageUser; }
|
||||
bool debugCheck() const { return m_debugCheck; }
|
||||
bool debugLeak() const { return m_debugLeak; }
|
||||
bool debugNondeterminism() const { return m_debugNondeterminism; }
|
||||
bool debugPartition() const { return m_debugPartition; }
|
||||
bool debugSelfTest() const { return m_debugSelfTest; }
|
||||
bool decoration() const { return m_decoration; }
|
||||
bool exe() const { return m_exe; }
|
||||
bool threadsDpiPure() const { return m_threadsDpiPure; }
|
||||
bool threadsDpiUnpure() const { return m_threadsDpiUnpure; }
|
||||
bool threadsCoarsen() const { return m_threadsCoarsen; }
|
||||
bool trace() const { return m_trace; }
|
||||
bool traceDups() const { return m_traceDups; }
|
||||
bool traceParams() const { return m_traceParams; }
|
||||
|
|
@ -245,6 +260,7 @@ class V3Options {
|
|||
bool pinsScBigUint() const { return m_pinsScBigUint; }
|
||||
bool pinsUint8() const { return m_pinsUint8; }
|
||||
bool profCFuncs() const { return m_profCFuncs; }
|
||||
bool profThreads() const { return m_profThreads; }
|
||||
bool allPublic() const { return m_public; }
|
||||
bool lintOnly() const { return m_lintOnly; }
|
||||
bool ignc() const { return m_ignc; }
|
||||
|
|
@ -266,6 +282,7 @@ class V3Options {
|
|||
int outputSplitCTrace() const { return m_outputSplitCTrace; }
|
||||
int pinsBv() const { return m_pinsBv; }
|
||||
int threads() const { return m_threads; }
|
||||
int threadsMaxMTasks() const { return m_threadsMaxMTasks; }
|
||||
bool mtasks() const { return (m_threads > 1); }
|
||||
int traceDepth() const { return m_traceDepth; }
|
||||
int traceMaxArray() const { return m_traceMaxArray; }
|
||||
|
|
@ -320,6 +337,7 @@ class V3Options {
|
|||
bool oLifePost() const { return m_oLifePost; }
|
||||
bool oLocalize() const { return m_oLocalize; }
|
||||
bool oInline() const { return m_oInline; }
|
||||
bool oReloop() const { return m_oReloop; }
|
||||
bool oReorder() const { return m_oReorder; }
|
||||
bool oSplit() const { return m_oSplit; }
|
||||
bool oSubst() const { return m_oSubst; }
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue