mirror of
https://github.com/verilator/verilator.git
synced 2026-10-02 16:13:34 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55f0d4ddd8 | ||
|
|
6d66fcaa57 | ||
|
|
26e79ca889 | ||
|
|
62eb247c1e | ||
|
|
c5fd583b2c | ||
|
|
117db3e11c | ||
|
|
3f82fd2f37 | ||
|
|
e9c46afcf7 | ||
|
|
3234fa15ef | ||
|
|
7ef84df852 | ||
|
|
4e2884b509 | ||
|
|
03100020ab | ||
|
|
8bfb5cc5e9 | ||
|
|
cf6d07aafa | ||
|
|
85c3179dbd | ||
|
|
bfbca34eb0 | ||
|
|
b73edc0564 | ||
|
|
6ba90e3a50 | ||
|
|
e8edbad18c | ||
|
|
c86fec5307 | ||
|
|
6e476255ca | ||
|
|
27af9b6b06 | ||
|
|
f8208fa961 | ||
|
|
87b9cb49ad | ||
|
|
b2da0772e6 | ||
|
|
4f73e0850e | ||
|
|
b6a39db627 | ||
|
|
3a83b06572 | ||
|
|
fe5bf01b25 | ||
|
|
5559ec903b | ||
|
|
18750028b2 | ||
|
|
6b88648c67 | ||
|
|
58ee803cb4 | ||
|
|
894ff73c7d | ||
|
|
8031f0ed7f | ||
|
|
3a56b5da47 | ||
|
|
fedcb6d26a | ||
|
|
eed3c5e543 | ||
|
|
e26ab67e25 | ||
|
|
e77b7427b4 | ||
|
|
475e4207cc | ||
|
|
71b2eed32c | ||
|
|
6cf50e6579 | ||
|
|
69468708e2 | ||
|
|
5da5678e64 | ||
|
|
a428e7f618 | ||
|
|
0eb5a0a539 | ||
|
|
a1c1ff9981 | ||
|
|
2029ade18c | ||
|
|
5f8f474c0c | ||
|
|
f705f9b275 | ||
|
|
91e706ec1f | ||
|
|
dcf351f847 | ||
|
|
06744b664a | ||
|
|
d3049d9c89 | ||
|
|
1f2abb9c0f | ||
|
|
9f0d7e50a5 | ||
|
|
5f262a8f11 | ||
|
|
f62bc6a2e5 | ||
|
|
d7e4bc1379 | ||
|
|
cd2d6575c6 | ||
|
|
b46f656d17 | ||
|
|
58fd602bbd | ||
|
|
9cc95704cd | ||
|
|
5f5a3dbb0a | ||
|
|
f8f53df4ec | ||
|
|
56b85cc63c | ||
|
|
90aca97e66 | ||
|
|
6ce2a52c5f | ||
|
|
1f56312132 | ||
|
|
3aa290cddb | ||
|
|
1d1eb4fedb | ||
|
|
266ff41386 | ||
|
|
02331e5536 | ||
|
|
621c51589a | ||
|
|
4a58e859a4 | ||
|
|
48d393d8f5 | ||
|
|
321552d998 | ||
|
|
a985a1f9f5 | ||
|
|
d532a36739 | ||
|
|
b631b5927b | ||
|
|
adb39ceb98 | ||
|
|
84b91b19ca | ||
|
|
aaea68d3d6 | ||
|
|
2accba2e71 | ||
|
|
8f4f4eb5ae | ||
|
|
c5aabb3c6e | ||
|
|
60c2d136e1 | ||
|
|
cae6ec16ba | ||
|
|
30601c77d4 | ||
|
|
b0f4cf3c9c | ||
|
|
c41dfcf6ad | ||
|
|
2e10555f03 | ||
|
|
6b2ee0fcf3 | ||
|
|
0dbdbffba7 | ||
|
|
9c5dd8d767 | ||
|
|
fff0ebb5f3 | ||
|
|
d34275150c | ||
|
|
d04eb977c2 | ||
|
|
fb4928b2f5 | ||
|
|
5c39420d91 | ||
|
|
14fcfd8a40 | ||
|
|
ff19dd94f9 | ||
|
|
b6913ff9b3 | ||
|
|
4573fd3069 | ||
|
|
6cf6d9f7e1 | ||
|
|
28e35a64ea | ||
|
|
091818483a | ||
|
|
3b43556c41 | ||
|
|
ed39c66715 | ||
|
|
446b0e4e5e | ||
|
|
6e3e8318d0 | ||
|
|
17b8b660f0 | ||
|
|
40bceea68a | ||
|
|
b655c17c09 | ||
|
|
a3813f94fc | ||
|
|
c70540a825 | ||
|
|
9caffe330b | ||
|
|
8d8c5da812 | ||
|
|
55bb766c15 | ||
|
|
c18df68ead | ||
|
|
3996e94c39 | ||
|
|
1bdf017f9e | ||
|
|
93790c1dc6 | ||
|
|
ba8c11b25d | ||
|
|
ca57edfa0b | ||
|
|
ccb6545c32 | ||
|
|
d146dbc9d7 | ||
|
|
b4eaaccc88 | ||
|
|
c9ed9e74f2 | ||
|
|
587c7c70f5 | ||
|
|
749ff021b6 | ||
|
|
85c5765c00 | ||
|
|
9e76078939 | ||
|
|
334e0e1ca6 | ||
|
|
dce4519995 | ||
|
|
45bbae80e7 | ||
|
|
d37709a922 | ||
|
|
2560ae9bc1 | ||
|
|
2bdd3ca353 | ||
|
|
8a8aab1aae | ||
|
|
85d790ff79 | ||
|
|
68afc96a9f | ||
|
|
94efaa7675 | ||
|
|
180af74a50 | ||
|
|
88af0d3509 | ||
|
|
97633f7bed | ||
|
|
053e016388 | ||
|
|
2d61e0270e | ||
|
|
3a23afb0bc |
@@ -3,6 +3,164 @@ Revision history for Verilator
|
||||
The contributors that suggested a given feature are shown in []. [by ...]
|
||||
indicates the contributor was also the author of the fix; Thanks!
|
||||
|
||||
* Verilator 3.866 2014-11-15
|
||||
|
||||
*** Fix +define+A+B to define A and B to match other simulators, bug847. [Adam Krolnik]
|
||||
|
||||
*** Add optimization of wires from arrayed cells, msg1447. [Jie Xu]
|
||||
|
||||
*** Add optimization of operators between concats, msg1447. [Jie Xu]
|
||||
|
||||
*** Add public enums, bug833. [Jonathon Donaldson]
|
||||
|
||||
*** Trace_off now operates on cells, bug826. [Lane Brooks]
|
||||
|
||||
**** Fix public parameters in unused packages, bug804. [Jonathon Donaldson]
|
||||
|
||||
**** Fix select when partially out-of-bound, bug823. [Cliffort Wolf]
|
||||
|
||||
**** Fix generate unrolling with function call, bug830. [Steven Slatter]
|
||||
|
||||
**** Fix cast-to-size context-determined sizing, bug828. [Geoff Barrett]
|
||||
|
||||
**** Fix not tracing modules following primitives, bug837. [Jie Xu]
|
||||
|
||||
**** Fix trace overflow on huge arrays, bug834. [Geoff Barrett]
|
||||
|
||||
**** Fix quoted comment slashes in defines, bug845. [Adam Krolnik]
|
||||
|
||||
|
||||
* Verilator 3.864 2014-09-21
|
||||
|
||||
*** Support power operator with real, bug809. [Jonathon Donaldson]
|
||||
|
||||
**** Improve verilator_profcfunc time attributions. [Jonathon Donaldson]
|
||||
|
||||
**** Fix duplicate anonymous structures in $root, bug788. [Bob Newgard]
|
||||
|
||||
**** Fix mis-optimization of bit-swap in wide signal, bug800. [Jie Xu]
|
||||
|
||||
**** Fix error when tracing public parameters, bug722. [Jonathon Donaldson]
|
||||
|
||||
**** Fix dpiGetContext in dotted scopes, bug740. [Geoff Barrett]
|
||||
|
||||
**** Fix over-shift structure optimization error, bug803. [Jeff Bush]
|
||||
|
||||
**** Fix optional parameter keyword in module #(), bug810. [Iztok Jeras]
|
||||
|
||||
**** Fix $warning/$error multi-argument ordering, bug816. [Jonathon Donaldson]
|
||||
|
||||
**** Fix clang warnings, bug818. [Iztok Jeras]
|
||||
|
||||
**** Fix string formats under deep expressions, bug820. [Iztok Jeras]
|
||||
|
||||
|
||||
* Verilator 3.862 2014-06-10
|
||||
|
||||
*** Using command line -Wno-{WARNING} now overrides file-local lint_on.
|
||||
|
||||
*** Add -P to suppress `line and blanks with preprocessing, bug781. [Derek Lockhart]
|
||||
|
||||
*** Support SV 2012 package import before port list.
|
||||
|
||||
**** Change SYMRSVDWORD to print as warning rather than error.
|
||||
|
||||
**** Fix seg-fault with variable of parameterized interface, bug692. [Jie Xu]
|
||||
|
||||
**** Fix false name conflict on cells in generate blocks, bug749. [Igor Lesik]
|
||||
|
||||
**** Fix pattern assignment to basic types, bug767. [Jie Xu]
|
||||
|
||||
**** Fix pattern assignment to conditionals, bug769. [Jie Xu]
|
||||
|
||||
**** Fix shift corner-cases, bug765, bug766, bug768, bug772, bug774, bug776. [Clifford Wolf]
|
||||
|
||||
**** Fix C compiler interpreting signing, bug773. [Clifford Wolf]
|
||||
|
||||
**** Fix late constant division by zero giving X error, bug775. [Clifford Wolf]
|
||||
|
||||
**** Fix gate primitives with arrays and non-arrayed pins.
|
||||
|
||||
**** Fix DETECTARRAY error on packed arrays, bug770. [Jie Xu]
|
||||
|
||||
**** Fix ENDLABEL warnings on escaped identifiers.
|
||||
|
||||
**** Fix string corruption, bug780. [Derek Lockhart]
|
||||
|
||||
|
||||
* Verilator 3.860 2014-05-11
|
||||
|
||||
** PSL is no longer supported, please use System Verilog assertions.
|
||||
|
||||
** Support '{} assignment pattern on arrays, bug355.
|
||||
|
||||
** Support streaming operators, bug649. [Glen Gibb]
|
||||
|
||||
** Fix expression problems with -Wno-WIDTH, bug729, bug736, bug737, bug759.
|
||||
Where WIDTH warnings were ignored this might result in different
|
||||
warning messages and results, though it should better match the spec.
|
||||
[Clifford Wolf]
|
||||
|
||||
*** Add --no-trace-params.
|
||||
|
||||
*** Add assertions on 'unique if', bug725. [Jeff Bush]
|
||||
|
||||
*** Add PINCONNECTEMPTY warning. [Holger Waechtler]
|
||||
|
||||
*** Support parameter arrays, bug683. [Jeremy Bennett]
|
||||
|
||||
*** Fix begin_keywords "1800+VAMS", msg1211.
|
||||
|
||||
**** Documentation fixes, bug723. [Glen Gibb]
|
||||
|
||||
**** Support {} in always sensitivity lists, bug745. [Igor Lesik]
|
||||
|
||||
**** Fix tracing of package variables and real arrays.
|
||||
|
||||
**** Fix tracing of packed arrays without --trace-structs, bug742. [Jie Xu]
|
||||
|
||||
**** Fix missing coverage line on else-if, bug727. [Sharad Bagri]
|
||||
|
||||
**** Fix modport function import not-found error.
|
||||
|
||||
**** Fix power operator calculation, bug730, bug735. [Clifford Wolf]
|
||||
|
||||
**** Fix reporting struct members as reserved words, bug741. [Chris Randall]
|
||||
|
||||
**** Fix change detection error on unions, bug758. [Jie Xu]
|
||||
|
||||
**** Fix -Wno-UNOPTFLAT change detection with 64-bits, bug762. [Clifford Wolf]
|
||||
|
||||
**** Fix shift-right optimization, bug763. [Clifford Wolf]
|
||||
|
||||
**** Fix Mac OS-X test issues. [Holger Waechtler]
|
||||
|
||||
**** Fix C++-2011 warnings.
|
||||
|
||||
|
||||
* Verilator 3.856 2014-03-11
|
||||
|
||||
*** Support case inside, bug708. [Jan Egil Ruud]
|
||||
|
||||
*** Add parameters into trace files, bug706. [Alex Solomatnikov]
|
||||
|
||||
**** Fix parsing "#0 'b0", bug256.
|
||||
|
||||
**** Fix array bound checks on real variables.
|
||||
|
||||
**** Fix --skip-identical mis-detecting on OS-X, bug707.
|
||||
|
||||
**** Fix missing VL_SHIFTRS_IQI with WIDTH warning, bug714. [Fabrizio Ferrandi]
|
||||
|
||||
**** Fix signed shift right optimization, bug715. [Fabrizio Ferrandi]
|
||||
|
||||
**** Fix internal error on "input x =" syntax error, bug716. [Lane Brooks]
|
||||
|
||||
**** Fix slice extraction from packed array, bug717. [Jan Egil Ruud]
|
||||
|
||||
**** Fix inside statement EQWILD error, bug718. [Jan Egil Ruud]
|
||||
|
||||
|
||||
* Verilator 3.855 2014-01-18
|
||||
|
||||
*** Support modport import, bug696. [Jeremy Bennett]
|
||||
@@ -94,17 +252,17 @@ indicates the contributor was also the author of the fix; Thanks!
|
||||
* Verilator 3.847 2013-05-11
|
||||
|
||||
*** Add ALWCOMBORDER warning. [KC Buckenmaier]
|
||||
|
||||
|
||||
*** Add --pins-sc-uint and --pins-sc-biguint, bug638. [Alex Hornung]
|
||||
|
||||
|
||||
**** Support "signal[vec]++".
|
||||
|
||||
|
||||
**** Fix simulation error when inputs and MULTIDRIVEN, bug634. [Ted Campbell]
|
||||
|
||||
|
||||
**** Fix module resolution with __, bug631. [Jason McMullan]
|
||||
|
||||
|
||||
**** Fix packed array non-zero right index select crash, bug642. [Krzysztof Jankowski]
|
||||
|
||||
|
||||
**** Fix nested union crash, bug643. [Krzysztof Jankowski]
|
||||
|
||||
|
||||
@@ -420,7 +578,7 @@ indicates the contributor was also the author of the fix; Thanks!
|
||||
*** Support $fopen and I/O with integer instead of `verilator_file_descriptor.
|
||||
|
||||
*** Support coverage in -cc and -sc output modes. [John Li]
|
||||
Note this requires SystemPerl 1.338 or newer.
|
||||
Note this requires SystemPerl 1.338 or newer.
|
||||
|
||||
**** Fix vpi_register_cb using bad s_cb_data, bug370. [by Thomas Watts]
|
||||
|
||||
|
||||
+3
-2
@@ -120,7 +120,8 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
||||
include/.*ignore \
|
||||
include/vltstd/*.[chv]* \
|
||||
.*attributes */.*attributes */*/.*attributes \
|
||||
src/.*ignore src/*.in src/*.cpp src/*.[chly] src/astgen src/bisonpre src/*fix \
|
||||
src/.*ignore src/*.in src/*.cpp src/*.[chly] \
|
||||
src/astgen src/bisonpre src/*fix src/cppcheck_filtered \
|
||||
src/.gdbinit \
|
||||
src/*.pl src/*.pod \
|
||||
test_*/.*ignore test_*/Makefile* test_*/*.cpp \
|
||||
@@ -400,7 +401,7 @@ endif
|
||||
endif
|
||||
|
||||
# Use --xml flag to see the cppcheck code to use for suppression
|
||||
CPPCHECK = cppcheck
|
||||
CPPCHECK = src/cppcheck_filtered
|
||||
CPPCHECK_FLAGS = --enable=all --inline-suppr --suppress=unusedScopedObject --suppress=cstyleCast
|
||||
CPPCHECK_FLAGS += --xml
|
||||
CPPCHECK_CPP = $(wildcard $(srcdir)/include/*.cpp $(srcdir)/src/*.cpp)
|
||||
|
||||
@@ -31,11 +31,13 @@ Configure/Make/Install
|
||||
* Distribute with flex/bison already expanded?
|
||||
Flex library not needed. Probably too difficult to be worth it.
|
||||
* Integrate SystemPerl coverage
|
||||
see the SystemPerl git branch coverage_only
|
||||
(Note in /usr/include there are no upper cased include files.)
|
||||
Coverage.pm -- Need all functionality, but in C?
|
||||
Coverage/Item.pm -- Need all functionality, but in C?
|
||||
Coverage/ItemKey.pm -- Need all functionality, but in C?
|
||||
sp_preproc -- Some steps in here need to be moved to generated C
|
||||
-- -- note uses Verilog::Getopt
|
||||
src/Sp.cpp -- n/a
|
||||
src/SpCommon.h -- mostly overlaps verilatedos.h
|
||||
src/SpCoverage.cpp/h -- All needed
|
||||
@@ -53,6 +55,7 @@ Testing:
|
||||
* 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 (4.000)
|
||||
|
||||
+87
-89
@@ -209,7 +209,7 @@ Verilator - Convert Verilog code to C++/SystemC
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Verilator converts synthesizable (not behavioral) Verilog code, plus some
|
||||
Synthesis, SystemVerilog and a small subset of Verilog AMS and Sugar/PSL
|
||||
Synthesis, SystemVerilog and a small subset of Verilog AMS
|
||||
assertions, into C++, SystemC or SystemPerl code. It is not a complete
|
||||
simulator, just a compiler.
|
||||
|
||||
@@ -259,7 +259,7 @@ descriptions in the next sections for more information.
|
||||
--coverage Enable all coverage
|
||||
--coverage-line Enable line coverage
|
||||
--coverage-toggle Enable toggle coverage
|
||||
--coverage-user Enable PSL/SVL user coverage
|
||||
--coverage-user Enable SVL user coverage
|
||||
--coverage-underscore Enable coverage of _signals
|
||||
-D<var>[=<value>] Set preprocessor define
|
||||
--debug Enable debugging
|
||||
@@ -267,7 +267,7 @@ descriptions in the next sections for more information.
|
||||
--debugi <level> Enable debugging at a specified level
|
||||
--debugi-<srcfile> <level> Enable debugging a source file at a level
|
||||
--default-language <lang> Default language to parse
|
||||
+define+<var>+<value> Set preprocessor define
|
||||
+define+<var>=<value> Set preprocessor define
|
||||
--dump-tree Enable dumping .tree files
|
||||
--dump-treei <level> Enable dumping .tree files at a level
|
||||
-E Preprocess, but do not compile
|
||||
@@ -303,6 +303,7 @@ descriptions in the next sections for more information.
|
||||
--output-split <bytes> Split .cpp files into pieces
|
||||
--output-split-cfuncs <statements> Split .cpp functions
|
||||
--output-split-ctrace <statements> Split tracing functions
|
||||
-P Disable line numbers and blanks with -E
|
||||
--pins-bv <bits> Specify types for top level ports
|
||||
--pins-sc-uint Specify types for top level ports
|
||||
--pins-sc-biguint Specify types for top level ports
|
||||
@@ -311,7 +312,6 @@ descriptions in the next sections for more information.
|
||||
--prefix <topname> Name of top level class
|
||||
--profile-cfuncs Name functions for profiling
|
||||
--private Debugging; see docs
|
||||
--psl Enable PSL parsing
|
||||
--public Debugging; see docs
|
||||
--report-unoptflat Extra diagnostics for UNOPTFLAT
|
||||
--savable Enable model save-restore
|
||||
@@ -325,6 +325,7 @@ descriptions in the next sections for more information.
|
||||
--trace-depth <levels> Depth of tracing
|
||||
--trace-max-array <depth> Maximum bit width for tracing
|
||||
--trace-max-width <width> Maximum array depth for tracing
|
||||
--trace-params Enable tracing parameters
|
||||
--trace-structs Enable tracing structure names
|
||||
--trace-underscore Enable tracing of _signals
|
||||
-U<var> Undefine preprocessor define
|
||||
@@ -409,8 +410,7 @@ C<+1364-1995ext+> etc. specify both the syntax I<and> semantics to be used.
|
||||
|
||||
=item --assert
|
||||
|
||||
Enable all assertions, includes enabling the --psl flag. (If psl is not
|
||||
desired, but other assertions are, use --assert --nopsl.)
|
||||
Enable all assertions.
|
||||
|
||||
See also --x-assign and --x-initial-edge; setting "--x-assign unique"
|
||||
and/or "--x-initial-edge" may be desirable.
|
||||
@@ -562,24 +562,26 @@ signals are not covered. See also --trace-underscore.
|
||||
=item --coverage-user
|
||||
|
||||
Enables user inserted functional coverage. Currently, all functional
|
||||
coverage points are specified using PSL which must be separately enabled
|
||||
with --psl.
|
||||
coverage points are specified using SVA which must be separately enabled
|
||||
with --assert.
|
||||
|
||||
For example, the following PSL statement will add a coverage point, with
|
||||
For example, the following statement will add a coverage point, with
|
||||
the comment "DefaultClock":
|
||||
|
||||
// psl default clock = posedge clk;
|
||||
// psl cover {cyc==9} report "DefaultClock,expect=1";
|
||||
DefaultClock: cover property (@(posedge clk) cyc==3);
|
||||
|
||||
=item -DI<var>=I<value>
|
||||
|
||||
Defines the given preprocessor symbol. Same as +define; +define is fairly
|
||||
standard across Verilog tools while -D is an alias for GCC compatibility.
|
||||
Defines the given preprocessor symbol, without allowing. Similar to
|
||||
+define; +define is fairly standard across Verilog tools while -D is an
|
||||
alias for GCC compatibility.
|
||||
|
||||
=item --debug
|
||||
|
||||
Select the debug built image of Verilator (if available), and enable more
|
||||
internal assertions, debugging messages, and intermediate form dump files.
|
||||
internal assertions (equivelent to C<--debug-check>), debugging messages
|
||||
(equivelent to C<--debugi 4>), and intermediate form dump files (equivilent
|
||||
to C<--dump-tree>).
|
||||
|
||||
=item --debug-check
|
||||
|
||||
@@ -591,9 +593,9 @@ changing debug verbosity. Enabled automatically when --debug specified.
|
||||
=item --debugi-<srcfile> <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal debugging level globally
|
||||
to the specified debug level (1-10) or set the specified source file to the
|
||||
specified level. Higher levels produce more detailed messages (plain
|
||||
C<--debug> is equivalent to C<--debugi 4>).
|
||||
to the specified debug level (1-10) or set the specified Verilator source
|
||||
file to the specified level (e.g. C<--debugi-V3Width 9>). Higher levels
|
||||
produce more detailed messages.
|
||||
|
||||
=item --default-language I<value>
|
||||
|
||||
@@ -614,10 +616,13 @@ used.
|
||||
If no language is specified, either by this flag or +I<lang>ext+ options,
|
||||
then the latest SystemVerilog language (IEEE 1800-2012) is used.
|
||||
|
||||
=item +define+I<var>+I<value>
|
||||
=item +define+I<var>=I<value>
|
||||
|
||||
Defines the given preprocessor symbol. Same as -D; +define is fairly
|
||||
standard across Verilog tools while -D is an alias for GCC compatibility.
|
||||
=item +define+I<var>=I<value>+I<var2>=I<value2>...
|
||||
|
||||
Defines the given preprocessor symbol, or multiple symbols if separated by
|
||||
plusses. Similar to -D; +define is fairly standard across Verilog tools
|
||||
while -D is an alias for GCC compatibility.
|
||||
|
||||
=item --dump-tree
|
||||
|
||||
@@ -630,7 +635,7 @@ large and not desired.
|
||||
=item --dump-treei <level>
|
||||
|
||||
Rarely needed. Enable writing .tree debug files with a specific dumping
|
||||
level, 0 disbles dumps and is equivelent to "--no-dump-tree". Level 9
|
||||
level, 0 disbles dumps and is equivalent to "--no-dump-tree". Level 9
|
||||
enables dumping of every stage.
|
||||
|
||||
=item -E
|
||||
@@ -783,8 +788,10 @@ Disables optimization of the model.
|
||||
|
||||
=item -O3
|
||||
|
||||
Enables slow optimizations. This may reduce simulation runtimes at the
|
||||
cost of compile time. This currently sets --inline-mult -1.
|
||||
Enables slow optimizations for the code Verilator itself generates (as
|
||||
opposed to "-CFLAGS -O3" which effects the C compiler's optimization. -O3
|
||||
may reduce simulation runtimes at the cost of compile time. This currently
|
||||
sets --inline-mult -1.
|
||||
|
||||
=item -OI<optimization-letter>
|
||||
|
||||
@@ -832,6 +839,11 @@ function.
|
||||
Enables splitting trace functions in the output .cpp/.sp files into
|
||||
multiple functions. Defaults to same setting as --output-split-cfuncs.
|
||||
|
||||
=item -P
|
||||
|
||||
With -E, disable generation of `line markers and blank lines, similar to
|
||||
GCC -P flag.
|
||||
|
||||
=item --pins64
|
||||
|
||||
Backward compatible alias for "--pins-bv 65". Note that's a 65, not a 64.
|
||||
@@ -902,12 +914,6 @@ statements.
|
||||
Opposite of --public. Is the default; this option exists for backwards
|
||||
compatibility.
|
||||
|
||||
=item --psl
|
||||
|
||||
Enable PSL parsing. Without this switch, PSL meta-comments are ignored.
|
||||
See the --assert flag to enable all assertions, and --coverage-user to
|
||||
enable functional coverage.
|
||||
|
||||
=item --public
|
||||
|
||||
This is only for historical debug use. Using it may result in
|
||||
@@ -929,7 +935,7 @@ break the loop.
|
||||
In addition produces a GraphViz DOT file of the entire strongly connected
|
||||
components within the source associated with each loop. This is produced
|
||||
irrespective of whether --dump-tree is set. Such graphs may help in
|
||||
analysing the problem, but can be very large indeed.
|
||||
analyzing the problem, but can be very large indeed.
|
||||
|
||||
Various commands exist for viewing and manipulating DOT files. For example
|
||||
the I<dot> command can be used to convert a DOT file to a PDF for
|
||||
@@ -1022,6 +1028,10 @@ Rarely needed. Specify the maximum bit width of a signal that may be
|
||||
traced. Defaults to 256, as tracing large vectors may greatly slow traced
|
||||
simulations.
|
||||
|
||||
=item --no-trace-params
|
||||
|
||||
Disable tracing of parameters.
|
||||
|
||||
=item --trace-structs
|
||||
|
||||
Enable tracing to show the name of packed structure, union, and packed
|
||||
@@ -1092,15 +1102,17 @@ supports which the older version does not support.
|
||||
|
||||
=item -Wno-I<message>
|
||||
|
||||
Disable the specified warning message.
|
||||
Disable the specified warning message. This will override any lint_on
|
||||
directives in the source, i.e. the warning will still not be printed.
|
||||
|
||||
=item -Wno-lint
|
||||
|
||||
Disable all lint related warning messages, and all style warnings. This is
|
||||
equivalent to "-Wno-ALWCOMBORDER -Wno-CASEINCOMPLETE -Wno-CASEOVERLAP
|
||||
-Wno-CASEX -Wno-CASEWITHX -Wno-CMPCONST -Wno-ENDLABEL -Wno-IMPLICIT
|
||||
-Wno-LITENDIAN -Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN
|
||||
-Wno-UNSIGNED -Wno-UNUSED -Wno-WIDTH" plus the list shown for Wno-style.
|
||||
-Wno-LITENDIAN -Wno-PINCONNECTEMPTY -Wno-PINMISSING -Wno-SYNCASYNCNET
|
||||
-Wno-UNDRIVEN -Wno-UNSIGNED -Wno-UNUSED -Wno-WIDTH" plus the list shown for
|
||||
Wno-style.
|
||||
|
||||
It is strongly recommended you cleanup your code rather than using this
|
||||
option, it is only intended to be use when running test-cases of code
|
||||
@@ -1110,8 +1122,8 @@ received from third parties.
|
||||
|
||||
Disable all code style related warning messages (note by default they are
|
||||
already disabled). This is equivalent to "-Wno-DECLFILENAME -Wno-DEFPARAM
|
||||
-Wno-INCABSPATH -Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN
|
||||
-Wno-UNUSED -Wno-VARHIDDEN".
|
||||
-Wno-INCABSPATH -Wno-PINCONNECTEMPTY -Wno-PINNOCONNECT -Wno-SYNCASYNCNET
|
||||
-Wno-UNDRIVEN -Wno-UNUSED -Wno-VARHIDDEN".
|
||||
|
||||
=item -Wno-fatal
|
||||
|
||||
@@ -1190,7 +1202,7 @@ so the above C<always> block would not trigger.
|
||||
|
||||
While it is not good practice, there are some designs that rely on X
|
||||
E<rarr> 0 triggering a C<negedge>, particularly in reset sequences. Using
|
||||
--x-initial-edge with Verilator will replicate this behaviour. It will also
|
||||
--x-initial-edge with Verilator will replicate this behavior. It will also
|
||||
ensure that X E<rarr> 1 triggers a C<posedge>.
|
||||
|
||||
B<Note.> Some users have reported that using this option can affect
|
||||
@@ -1921,6 +1933,9 @@ Disables the specified lint warning, in the specified filename (or wildcard
|
||||
with '*' or '?', or all files if omitted) and range of line numbers (or all
|
||||
lines if omitted).
|
||||
|
||||
Using '*' will override any lint_on directives in the source, i.e. the
|
||||
warning will still not be printed.
|
||||
|
||||
If the -msg is omitted, all lint warnings are disabled. This will override
|
||||
all later lint warning enables for the specified region.
|
||||
|
||||
@@ -1975,7 +1990,7 @@ please file a bug if a feature you need is missing.
|
||||
|
||||
Verilator implements a very small subset of Verilog AMS (Verilog Analog and
|
||||
Mixed-Signal Extensions) with the subset corresponding to those VMS
|
||||
keywords with near equivelents in the Verilog 2005 or SystemVerilog 2009
|
||||
keywords with near equivalents in the Verilog 2005 or SystemVerilog 2009
|
||||
languages.
|
||||
|
||||
AMS parsing is enabled with "--language VAMS" or "--language 1800+VAMS".
|
||||
@@ -1983,34 +1998,6 @@ AMS parsing is enabled with "--language VAMS" or "--language 1800+VAMS".
|
||||
At present Verilator implements ceil, exp, floor, ln, log, pow, sqrt,
|
||||
string, and wreal.
|
||||
|
||||
=head2 Sugar/PSL Support
|
||||
|
||||
Most future work is being directed towards improving SystemVerilog
|
||||
assertions instead of PSL. If you are using these PSL features, please
|
||||
contact the author as they may be depreciated in future versions.
|
||||
|
||||
With the --assert switch, Verilator enables support of the Property
|
||||
Specification Language (PSL), specifically the simple PSL subset without
|
||||
time-branching primitives. Verilator currently only converts PSL
|
||||
assertions to simple "if (...) error" statements, and coverage statements
|
||||
to increment the line counters described in the coverage section.
|
||||
|
||||
Verilator implements these keywords: assert, assume (same as assert),
|
||||
default (for clocking), countones, cover, isunknown, onehot, onehot0,
|
||||
report, and true.
|
||||
|
||||
Verilator implements these operators: -> (logical if).
|
||||
|
||||
Verilator does not support SEREs yet. All assertion and coverage
|
||||
statements must be simple expressions that complete in one cycle. PSL
|
||||
vmode/vprop/vunits are not supported. PSL statements must be in the module
|
||||
they reference, at the module level where you would put an
|
||||
initial... statement.
|
||||
|
||||
Verilator only supports (posedge CLK) or (negedge CLK), where CLK is the
|
||||
name of a one bit signal. You may not use arbitrary expressions as
|
||||
assertion clocks.
|
||||
|
||||
=head2 Synthesis Directive Assertion Support
|
||||
|
||||
With the --assert switch, Verilator reads any "//synopsys full_case" or
|
||||
@@ -2023,9 +2010,8 @@ appropriate code to detect failing cases at runtime and print an "Assertion
|
||||
failed" error message.
|
||||
|
||||
Verilator likewise also asserts any "unique" or "priority" SystemVerilog
|
||||
keywords on case statements. However, "unique if" and "priority if" are
|
||||
currently simply ignored.
|
||||
|
||||
keywords on case statement, as well as "unique" on if statements.
|
||||
However, "priority if" is currently simply ignored.
|
||||
|
||||
=head1 LANGUAGE EXTENSIONS
|
||||
|
||||
@@ -2051,13 +2037,6 @@ supported by Verilator since 2006!)
|
||||
|
||||
This will report an error when encountered, like C++'s #error.
|
||||
|
||||
=item _(I<expr>)
|
||||
|
||||
A underline followed by an expression in parenthesis returns a Verilog
|
||||
expression. This is different from normal parenthesis in special contexts,
|
||||
such as PSL expressions, and can be used to embed bit concatenation ({})
|
||||
inside of PSL statements.
|
||||
|
||||
=item $c(I<string>, ...);
|
||||
|
||||
The string will be embedded directly in the output C++ code at the point
|
||||
@@ -2276,6 +2255,14 @@ reduce the size of the final executable when a task is used a very large
|
||||
number of times. For this flag to work, the task and tasks below it must
|
||||
be pure; they cannot reference any variables outside the task itself.
|
||||
|
||||
=item /*verilator public*/ (typedef enum)
|
||||
|
||||
Used after an enum typedef declaration to indicate the emitted C code
|
||||
should have the enum values visible. Due to C++ language restrictions, this
|
||||
may only be used on 64-bit or narrower integral enumerations.
|
||||
|
||||
typedef enum logic [2:0] { ZERO = 3'b0 } pub_t /*verilator public*/;
|
||||
|
||||
=item /*verilator public*/ (variable)
|
||||
|
||||
Used after an input, output, register, or wire declaration to indicate the
|
||||
@@ -2363,12 +2350,14 @@ behavior. See the test_regress/t/t_dpi_display.v file for an example.
|
||||
=item /*verilator tracing_off*/
|
||||
|
||||
Disable waveform tracing for all future signals that are declared in this
|
||||
module. Often this is placed just after a primitive's module statement, so
|
||||
that the entire module is not traced.
|
||||
module, or cells below this module. Often this is placed just after a
|
||||
primitive's module statement, so that the entire module and cells below it
|
||||
are not traced.
|
||||
|
||||
=item /*verilator tracing_on*/
|
||||
|
||||
Re-enable waveform tracing for all future signals that are declared.
|
||||
Re-enable waveform tracing for all future signals or cells that are
|
||||
declared.
|
||||
|
||||
=back
|
||||
|
||||
@@ -2471,7 +2460,7 @@ reset works. (Note this is what the hardware will really do.) In
|
||||
practice, just setting all variables to one at startup finds most problems.
|
||||
|
||||
B<Note.> --x-assign applies to variables explicitly initialized or assigned to
|
||||
X. Unititialized clocks are initialized to zero, while all other state holding
|
||||
X. Uninitialized clocks are initialized to zero, while all other state holding
|
||||
variables are initialized to a random value.
|
||||
|
||||
Event driven simulators will generally trigger an edge on a transition from X
|
||||
@@ -2997,8 +2986,8 @@ simulators.
|
||||
Warns that an `include filename specifies an absolute path. This means the
|
||||
code will not work on any other system with a different file system layout.
|
||||
Instead of using absolute paths, relative paths (preferably without any
|
||||
directory specified whatever) should be used, and +include used on the
|
||||
command line to specify the top include source directory.
|
||||
directory specified whatever) should be used, and +incdir used on the
|
||||
command line to specify the top include source directories.
|
||||
|
||||
Disabled by default as this is a code style warning; it will simulate
|
||||
correctly.
|
||||
@@ -3049,11 +3038,21 @@ not really needed. The best solution is to insure that each module is in a
|
||||
unique file by the same name. Otherwise, make sure all library files are
|
||||
read in as libraries with -v, instead of automatically with -y.
|
||||
|
||||
=item PINCONNECTEMPTY
|
||||
|
||||
Warns that a cell instantiation has a pin which is connected to
|
||||
.pin_name(), e.g. not another signal, but with an explicit mention of the
|
||||
pin. It may be desirable to disable PINCONNECTEMPTY, as this indicates
|
||||
intention to have a no-connect.
|
||||
|
||||
Disabled by default as this is a code style warning; it will simulate
|
||||
correctly.
|
||||
|
||||
=item PINMISSING
|
||||
|
||||
Warns that a module has a pin which is not mentioned in a cell
|
||||
instantiation. If a pin is not missing it should still be specified on the
|
||||
cell declaration with a empty connection,using "(.pin_name())".
|
||||
cell declaration with a empty connection, using "(.pin_name())".
|
||||
|
||||
Ignoring this warning will only suppress the lint check, it will simulate
|
||||
correctly.
|
||||
@@ -3117,10 +3116,9 @@ simulators.
|
||||
|
||||
=item SYMRSVDWORD
|
||||
|
||||
Error that a symbol matches a C++ reserved word and using this as a symbol
|
||||
name would result in odd C compiler errors. You may disable this error
|
||||
message as you would disable warnings, but the symbol will be renamed by
|
||||
Verilator to avoid the conflict.
|
||||
Warning that a symbol matches a C++ reserved word and using this as a symbol
|
||||
name would result in odd C compiler errors. You may disable this warning,
|
||||
but the symbol will be renamed by Verilator to avoid the conflict.
|
||||
|
||||
=item SYNCASYNCNET
|
||||
|
||||
@@ -3409,7 +3407,7 @@ so your simulator shouldn't have to be -- and Verilator is closer to the
|
||||
synthesis interpretation, so this is a good thing for getting working
|
||||
silicon.
|
||||
|
||||
=item Will Verilator output remain under my own copyright?
|
||||
=item Will Verilator output remain under my own license?
|
||||
|
||||
Yes, it's just like using GCC on your programs; this is why Verilator uses
|
||||
the "GNU *Lesser* Public License Version 3" instead of the more typical
|
||||
@@ -3543,8 +3541,8 @@ coverage. Both require the SystemPerl package to be installed but do not
|
||||
require use of the SystemPerl output mode.
|
||||
|
||||
First, run verilator with the --coverage option. If you're using your own
|
||||
makefile, compile the model with the GCC flag -DSP_COVERAGE_ENABLE (if
|
||||
using Verilator's, it will do this for you.)
|
||||
makefile, compile the model with the GCC flag -DSP_COVERAGE (if using
|
||||
Verilator's, it will do this for you.)
|
||||
|
||||
Run your tests in different directories. Each test will create a
|
||||
logs/coverage.pl file.
|
||||
|
||||
+15
-1
@@ -118,6 +118,18 @@ sub profcfunc {
|
||||
$groups{type}{"Common code under $design"} += $pct;
|
||||
$groups{design}{$design} += $pct;
|
||||
$groups{module}{$design." common code"} += $pct;
|
||||
} elsif ($func =~ /^VL_[A-Z0-9_]+/
|
||||
|| $func =~ /^_?vl_[a-zA-Z0-9_]+/
|
||||
|| $func =~ /^verilated/i) {
|
||||
$vfunc = sprintf("VLib %s", $func);
|
||||
$groups{type}{'VLib'} += $pct;
|
||||
$groups{design}{'VLib'} += $pct;
|
||||
$groups{module}{'VLib'} += $pct;
|
||||
} elsif ($func =~ /^_mcount_private/) {
|
||||
$vfunc = sprintf("Prof %s", $func);
|
||||
$groups{type}{'Prof'} += $pct;
|
||||
$groups{design}{'Prof'} += $pct;
|
||||
$groups{module}{'Prof'} += $pct;
|
||||
} else {
|
||||
$vfunc = sprintf("C++ %s", $func);
|
||||
$groups{type}{'C++'} += $pct;
|
||||
@@ -129,7 +141,7 @@ sub profcfunc {
|
||||
}
|
||||
|
||||
|
||||
foreach my $type qw(type design module) {
|
||||
foreach my $type (qw(type design module)) {
|
||||
my $missing = 100;
|
||||
foreach (sort (keys %{$groups{$type}})) {
|
||||
$missing -= $groups{$type}{$_};
|
||||
@@ -150,8 +162,10 @@ sub profcfunc {
|
||||
print("Verilog code profile:\n");
|
||||
print(" These are split into three categories:\n");
|
||||
print(" C++: Time in non-Verilated C++ code\n");
|
||||
print(" Prof: Time in profile overhead\n");
|
||||
print(" VBlock: Time attributable to a block in a Verilog file and line\n");
|
||||
print(" VCommon: Time in a Verilated module, due to all parts of the design\n");
|
||||
print(" VLib: Time in Verilated common libraries, called by the Verilated code\n");
|
||||
print("\n");
|
||||
|
||||
print(" % cumulative self \n");
|
||||
|
||||
+4
-2
@@ -6,7 +6,7 @@
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[3.855 2014-01-18])
|
||||
AC_INIT([Verilator],[3.866 2014-11-15])
|
||||
AC_CONFIG_HEADER(src/config_build.h)
|
||||
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h)
|
||||
|
||||
@@ -122,15 +122,17 @@ AC_DEFUN([_MY_CXX_CHECK_OPT],
|
||||
|
||||
# For example, -Wno-div-by-zero isn't in 4.1.2
|
||||
_MY_CXX_CHECK_OPT(-Wno-char-subscripts)
|
||||
_MY_CXX_CHECK_OPT(-Wno-parentheses-equality)
|
||||
# Random code often does / 0. Unfortunately VL_DIV_I(0,0) will warn
|
||||
# without this flag, even though there's a conditional to prevent the divide.
|
||||
# We still don't add no-div-by-zero as it throws message to stdout, though doesn't die.
|
||||
#_MY_CXX_CHECK_OPT(-Wno-div-by-zero)
|
||||
_MY_CXX_CHECK_OPT(-Wno-sign-compare)
|
||||
_MY_CXX_CHECK_OPT(-Wno-uninitialized)
|
||||
_MY_CXX_CHECK_OPT(-Wno-unused-but-set-variable)
|
||||
_MY_CXX_CHECK_OPT(-Wno-unused-parameter)
|
||||
_MY_CXX_CHECK_OPT(-Wno-unused-variable)
|
||||
_MY_CXX_CHECK_OPT(-Wno-unused-but-set-variable)
|
||||
_MY_CXX_CHECK_OPT(-fbracket-depth=4096)
|
||||
AC_SUBST(CFG_CXXFLAGS_NO_UNUSED)
|
||||
|
||||
# Checks for library functions.
|
||||
|
||||
@@ -74,6 +74,9 @@ void vl_fatal (const char* filename, int linenum, const char* hier, const char*
|
||||
Verilated::gotFinish(true);
|
||||
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg);
|
||||
Verilated::flushCall();
|
||||
|
||||
VL_PRINTF("Aborting...\n");
|
||||
Verilated::flushCall(); // Second flush in case VL_PRINTF does something needing a flush
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
@@ -207,7 +210,8 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, boo
|
||||
vluint32_t vn[VL_MULS_MAX_WORDS+1]; // v normalized
|
||||
|
||||
// Zero for ease of debugging and to save having to zero for shifts
|
||||
for (int i=0; i<words; i++) { un[i]=vn[i]=0; }
|
||||
// Note +1 as loop will use extra word
|
||||
for (int i=0; i<words+1; i++) { un[i]=vn[i]=0; }
|
||||
|
||||
// Algorithm requires divisor MSB to be set
|
||||
// Copy and shift to normalize divisor so MSB of vn[vw-1] is set
|
||||
@@ -1249,9 +1253,9 @@ void VerilatedScope::scopeDump() const {
|
||||
m_callbacksp[i], VerilatedImp::exportName(i));
|
||||
}
|
||||
}
|
||||
if (varsp()) {
|
||||
for (VerilatedVarNameMap::const_iterator it = varsp()->begin();
|
||||
it != varsp()->end(); ++it) {
|
||||
if (VerilatedVarNameMap* varsp = this->varsp()) {
|
||||
for (VerilatedVarNameMap::const_iterator it = varsp->begin();
|
||||
it != varsp->end(); ++it) {
|
||||
VL_PRINTF(" VAR %p: %s\n", &(it->second), it->first);
|
||||
}
|
||||
}
|
||||
|
||||
+169
-15
@@ -1163,7 +1163,10 @@ static inline WDataOutP VL_MODDIVS_WWW(int lbits, WDataOutP owp,WDataInP lwp,WDa
|
||||
}
|
||||
}
|
||||
|
||||
#define VL_POW_QQI(obits,lbits,rbits,lhs,rhs) VL_POW_QQQ(obits,lbits,rbits,lhs,rhs)
|
||||
|
||||
static inline IData VL_POW_III(int, int, int rbits, IData lhs, IData rhs) {
|
||||
if (VL_UNLIKELY(rhs==0)) return 1;
|
||||
if (VL_UNLIKELY(lhs==0)) return 0;
|
||||
IData power = lhs;
|
||||
IData out = 1;
|
||||
@@ -1173,10 +1176,8 @@ static inline IData VL_POW_III(int, int, int rbits, IData lhs, IData rhs) {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#define VL_POW_QQI(obits,lbits,rbits,lhs,rhs) VL_POW_QQQ(obits,lbits,rbits,lhs,rhs)
|
||||
|
||||
static inline QData VL_POW_QQQ(int, int, int rbits, QData lhs, QData rhs) {
|
||||
if (VL_UNLIKELY(rhs==0)) return 1;
|
||||
if (VL_UNLIKELY(lhs==0)) return 0;
|
||||
QData power = lhs;
|
||||
QData out = VL_ULL(1);
|
||||
@@ -1187,6 +1188,36 @@ static inline QData VL_POW_QQQ(int, int, int rbits, QData lhs, QData rhs) {
|
||||
return out;
|
||||
}
|
||||
|
||||
#define VL_POWSS_QQI(obits,lbits,rbits,lhs,rhs,lsign,rsign) VL_POWSS_QQQ(obits,lbits,rbits,lhs,rhs,lsign,rsign)
|
||||
|
||||
static inline IData VL_POWSS_III(int obits, int, int rbits, IData lhs, IData rhs, bool lsign, bool rsign) {
|
||||
if (VL_UNLIKELY(rhs==0)) return 1;
|
||||
if (rsign && VL_SIGN_I(rbits, rhs)) {
|
||||
if (lhs==0) return 0; // "X"
|
||||
else if (lhs==1) return 1;
|
||||
else if (lsign && lhs==VL_MASK_I(obits)) { //-1
|
||||
if (rhs & 1) return VL_MASK_I(obits); // -1^odd=-1
|
||||
else return 1; // -1^even=1
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return VL_POW_III(obits, obits, rbits, lhs, rhs);
|
||||
}
|
||||
|
||||
static inline QData VL_POWSS_QQQ(int obits, int, int rbits, QData lhs, QData rhs, bool lsign, bool rsign) {
|
||||
if (VL_UNLIKELY(rhs==0)) return 1;
|
||||
if (rsign && VL_SIGN_I(rbits, rhs)) {
|
||||
if (lhs==0) return 0; // "X"
|
||||
else if (lhs==1) return 1;
|
||||
else if (lsign && lhs==VL_MASK_I(obits)) { //-1
|
||||
if (rhs & 1) return VL_MASK_I(obits); // -1^odd=-1
|
||||
else return 1; // -1^even=1
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return VL_POW_QQQ(obits, obits, rbits, lhs, rhs);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Concat/replication
|
||||
|
||||
@@ -1327,6 +1358,120 @@ static inline WDataOutP VL_REPLICATE_WWI(int obits, int lbits, int, WDataOutP ow
|
||||
return(owp);
|
||||
}
|
||||
|
||||
// Left stream operator. Output will always be clean. LHS and RHS must be clean.
|
||||
// Special "fast" versions for slice sizes that are a power of 2. These use
|
||||
// shifts and masks to execute faster than the slower for-loop approach where a
|
||||
// subset of bits is copied in during each iteration.
|
||||
static inline IData VL_STREAML_FAST_III(int, int lbits, int, IData ld, IData rd_log2) {
|
||||
// Pre-shift bits in most-significant slice:
|
||||
//
|
||||
// If lbits is not a multiple of the slice size (i.e., lbits % rd != 0),
|
||||
// then we end up with a "gap" in our reversed result. For example, if we
|
||||
// have a 5-bit Verlilog signal (lbits=5) in an 8-bit C data type:
|
||||
//
|
||||
// ld = ---43210
|
||||
//
|
||||
// (where numbers are the Verilog signal bit numbers and '-' is an unused bit).
|
||||
// Executing the switch statement below with a slice size of two (rd=2,
|
||||
// rd_log2=1) produces:
|
||||
//
|
||||
// ret = 1032-400
|
||||
//
|
||||
// Pre-shifting the bits in the most-significant slice allows us to avoid
|
||||
// this gap in the shuffled data:
|
||||
//
|
||||
// ld_adjusted = --4-3210
|
||||
// ret = 10324---
|
||||
IData ret = ld;
|
||||
if (rd_log2) {
|
||||
vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits
|
||||
vluint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS)
|
||||
IData msbMask = VL_MASK_I(lbitsRem) << lbitsFloor; // mask to sel only bits in MSS
|
||||
ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem));
|
||||
}
|
||||
switch (rd_log2) {
|
||||
case 0:
|
||||
ret = ((ret >> 1) & VL_UL(0x55555555)) | ((ret & VL_UL(0x55555555)) << 1); // FALLTHRU
|
||||
case 1:
|
||||
ret = ((ret >> 2) & VL_UL(0x33333333)) | ((ret & VL_UL(0x33333333)) << 2); // FALLTHRU
|
||||
case 2:
|
||||
ret = ((ret >> 4) & VL_UL(0x0f0f0f0f)) | ((ret & VL_UL(0x0f0f0f0f)) << 4); // FALLTHRU
|
||||
case 3:
|
||||
ret = ((ret >> 8) & VL_UL(0x00ff00ff)) | ((ret & VL_UL(0x00ff00ff)) << 8); // FALLTHRU
|
||||
case 4:
|
||||
ret = ((ret >> 16) | (ret << 16));
|
||||
}
|
||||
return ret >> (VL_WORDSIZE - lbits);
|
||||
}
|
||||
|
||||
static inline QData VL_STREAML_FAST_QQI(int, int lbits, int, QData ld, IData rd_log2) {
|
||||
// Pre-shift bits in most-significant slice (see comment in VL_STREAML_FAST_III)
|
||||
QData ret = ld;
|
||||
if (rd_log2) {
|
||||
vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2);
|
||||
vluint32_t lbitsRem = lbits - lbitsFloor;
|
||||
QData msbMask = VL_MASK_Q(lbitsRem) << lbitsFloor;
|
||||
ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_ULL(1) << rd_log2) - lbitsRem));
|
||||
}
|
||||
switch (rd_log2) {
|
||||
case 0:
|
||||
ret = ((ret >> 1) & VL_ULL(0x5555555555555555)) | ((ret & VL_ULL(0x5555555555555555)) << 1); // FALLTHRU
|
||||
case 1:
|
||||
ret = ((ret >> 2) & VL_ULL(0x3333333333333333)) | ((ret & VL_ULL(0x3333333333333333)) << 2); // FALLTHRU
|
||||
case 2:
|
||||
ret = ((ret >> 4) & VL_ULL(0x0f0f0f0f0f0f0f0f)) | ((ret & VL_ULL(0x0f0f0f0f0f0f0f0f)) << 4); // FALLTHRU
|
||||
case 3:
|
||||
ret = ((ret >> 8) & VL_ULL(0x00ff00ff00ff00ff)) | ((ret & VL_ULL(0x00ff00ff00ff00ff)) << 8); // FALLTHRU
|
||||
case 4:
|
||||
ret = ((ret >> 16) & VL_ULL(0x0000ffff0000ffff)) | ((ret & VL_ULL(0x0000ffff0000ffff)) << 16); // FALLTHRU
|
||||
case 5:
|
||||
ret = ((ret >> 32) | (ret << 32));
|
||||
}
|
||||
return ret >> (VL_QUADSIZE - lbits);
|
||||
}
|
||||
|
||||
// Regular "slow" streaming operators
|
||||
static inline IData VL_STREAML_III(int, int lbits, int, IData ld, IData rd) {
|
||||
IData ret = 0;
|
||||
// Slice size should never exceed the lhs width
|
||||
IData mask = VL_MASK_I(rd);
|
||||
for (int istart=0; istart<lbits; istart+=rd) {
|
||||
int ostart=lbits-rd-istart;
|
||||
ostart = ostart > 0 ? ostart : 0;
|
||||
ret |= ((ld >> istart) & mask) << ostart;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline QData VL_STREAML_QQI(int, int lbits, int, QData ld, IData rd) {
|
||||
QData ret = 0;
|
||||
// Slice size should never exceed the lhs width
|
||||
QData mask = VL_MASK_Q(rd);
|
||||
for (int istart=0; istart<lbits; istart+=rd) {
|
||||
int ostart=lbits-rd-istart;
|
||||
ostart = ostart > 0 ? ostart : 0;
|
||||
ret |= ((ld >> istart) & mask) << ostart;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline WDataOutP VL_STREAML_WWI(int, int lbits, int, WDataOutP owp, WDataInP lwp, IData rd) {
|
||||
VL_ZERO_RESET_W(lbits, owp);
|
||||
// Slice size should never exceed the lhs width
|
||||
int ssize = (rd < (IData)lbits) ? rd : ((IData)lbits);
|
||||
for (int istart=0; istart<lbits; istart+=rd) {
|
||||
int ostart=lbits-rd-istart;
|
||||
ostart = ostart > 0 ? ostart : 0;
|
||||
for (int sbit=0; sbit<ssize && sbit<lbits-istart; sbit++) {
|
||||
// Extract a single bit from lwp and shift it to the correct
|
||||
// location for owp.
|
||||
WData bit= ((lwp[VL_BITWORD_I(istart+sbit)] >> VL_BITBIT_I(istart+sbit)) & 1) << VL_BITBIT_I(ostart+sbit);
|
||||
owp[VL_BITWORD_I(ostart+sbit)] |= bit;
|
||||
}
|
||||
}
|
||||
return owp;
|
||||
}
|
||||
|
||||
// Because concats are common and wide, it's valuable to always have a clean output.
|
||||
// Thus we specify inputs must be clean, so we don't need to clean the output.
|
||||
// Note the bit shifts are always constants, so the adds in these constify out.
|
||||
@@ -1413,7 +1558,7 @@ static inline void _VL_SHIFTL_INPLACE_W(int obits,WDataOutP iowp,IData rd/*1 or
|
||||
static inline WDataOutP VL_SHIFTL_WWI(int obits,int,int,WDataOutP owp,WDataInP lwp, IData rd) {
|
||||
int word_shift = VL_BITWORD_I(rd);
|
||||
int bit_shift = VL_BITBIT_I(rd);
|
||||
if ((int)rd >= obits) {
|
||||
if (rd >= (IData)obits) { // rd may be huge with MSB set
|
||||
for (int i=0; i < VL_WORDS_I(obits); i++) owp[i] = 0;
|
||||
} else if (bit_shift==0) { // Aligned word shift (<<0,<<32,<<64 etc)
|
||||
for (int i=0; i < word_shift; i++) owp[i] = 0;
|
||||
@@ -1431,7 +1576,7 @@ static inline WDataOutP VL_SHIFTL_WWI(int obits,int,int,WDataOutP owp,WDataInP l
|
||||
static inline WDataOutP VL_SHIFTR_WWI(int obits,int,int,WDataOutP owp,WDataInP lwp, IData rd) {
|
||||
int word_shift = VL_BITWORD_I(rd); // Maybe 0
|
||||
int bit_shift = VL_BITBIT_I(rd);
|
||||
if ((int)rd >= obits) {
|
||||
if (rd >= (IData)obits) { // rd may be huge with MSB set
|
||||
for (int i=0; i < VL_WORDS_I(obits); i++) owp[i] = 0;
|
||||
} else if (bit_shift==0) { // Aligned word shift (>>0,>>32,>>64 etc)
|
||||
int copy_words = (VL_WORDS_I(obits)-word_shift);
|
||||
@@ -1455,29 +1600,37 @@ static inline WDataOutP VL_SHIFTR_WWI(int obits,int,int,WDataOutP owp,WDataInP l
|
||||
}
|
||||
|
||||
// EMIT_RULE: VL_SHIFTRS: oclean=false; lclean=clean, rclean==clean;
|
||||
static inline IData VL_SHIFTRS_III(int obits, int, int, IData lhs, IData rhs) {
|
||||
static inline IData VL_SHIFTRS_III(int obits, int lbits, int, IData lhs, IData rhs) {
|
||||
// Note the C standard does not specify the >> operator as a arithmetic shift!
|
||||
IData sign = -(lhs >> (obits-1)); // ffff_ffff if negative
|
||||
IData signext = ~(VL_MASK_I(obits) >> rhs); // One with bits where we've shifted "past"
|
||||
// IEEE says signed if output signed, but bit position from lbits;
|
||||
// must use lbits for sign; lbits might != obits,
|
||||
// an EXTEND(SHIFTRS(...)) can became a SHIFTRS(...) within same 32/64 bit word length
|
||||
IData sign = -(lhs >> (lbits-1)); // ffff_ffff if negative
|
||||
IData signext = ~(VL_MASK_I(lbits) >> rhs); // One with bits where we've shifted "past"
|
||||
return (lhs >> rhs) | (sign & VL_CLEAN_II(obits,obits,signext));
|
||||
}
|
||||
static inline QData VL_SHIFTRS_QQI(int obits, int, int, QData lhs, IData rhs) {
|
||||
QData sign = -(lhs >> (obits-1));
|
||||
QData signext = ~(VL_MASK_Q(obits) >> rhs);
|
||||
static inline QData VL_SHIFTRS_QQI(int obits, int lbits, int, QData lhs, IData rhs) {
|
||||
QData sign = -(lhs >> (lbits-1));
|
||||
QData signext = ~(VL_MASK_Q(lbits) >> rhs);
|
||||
return (lhs >> rhs) | (sign & VL_CLEAN_QQ(obits,obits,signext));
|
||||
}
|
||||
static inline WDataOutP VL_SHIFTRS_WWI(int obits,int,int,WDataOutP owp,WDataInP lwp, IData rd) {
|
||||
static inline IData VL_SHIFTRS_IQI(int obits, int lbits, int rbits, QData lhs, IData rhs) {
|
||||
return (IData)(VL_SHIFTRS_QQI(obits, lbits, rbits, lhs, rhs));
|
||||
}
|
||||
static inline WDataOutP VL_SHIFTRS_WWI(int obits,int lbits,int,WDataOutP owp,WDataInP lwp, IData rd) {
|
||||
int word_shift = VL_BITWORD_I(rd);
|
||||
int bit_shift = VL_BITBIT_I(rd);
|
||||
int lmsw = VL_WORDS_I(obits)-1;
|
||||
IData sign = VL_SIGNONES_I(obits,lwp[lmsw]);
|
||||
if ((int)rd >= obits) {
|
||||
IData sign = VL_SIGNONES_I(lbits,lwp[lmsw]);
|
||||
if (rd >= (IData)obits) { // Shifting past end, sign in all of lbits
|
||||
for (int i=0; i <= lmsw; i++) owp[i] = sign;
|
||||
owp[lmsw] &= VL_MASK_I(lbits);
|
||||
} else if (bit_shift==0) { // Aligned word shift (>>0,>>32,>>64 etc)
|
||||
int copy_words = (VL_WORDS_I(obits)-word_shift);
|
||||
for (int i=0; i < copy_words; i++) owp[i] = lwp[i+word_shift];
|
||||
if (copy_words>=0) owp[copy_words-1] |= ~VL_MASK_I(obits) & sign;
|
||||
for (int i=copy_words; i < VL_WORDS_I(obits); i++) owp[i] = sign;
|
||||
owp[lmsw] &= VL_MASK_I(lbits);
|
||||
} else {
|
||||
int loffset = rd & VL_SIZEBITS_I;
|
||||
int nbitsonright = 32-loffset; // bits that end up in lword (know loffset!=0)
|
||||
@@ -1492,6 +1645,7 @@ static inline WDataOutP VL_SHIFTRS_WWI(int obits,int,int,WDataOutP owp,WDataInP
|
||||
}
|
||||
if (words) owp[words-1] |= sign & ~VL_MASK_I(obits-loffset);
|
||||
for (int i=words; i<VL_WORDS_I(obits); i++) owp[i] = sign;
|
||||
owp[lmsw] &= VL_MASK_I(lbits);
|
||||
}
|
||||
return(owp);
|
||||
}
|
||||
@@ -1507,7 +1661,7 @@ static inline WDataOutP VL_SHIFTRS_WWI(int obits,int,int,WDataOutP owp,WDataInP
|
||||
|
||||
static inline IData VL_BITSEL_IWII(int, int lbits, int, int, WDataInP lwp, IData rd) {
|
||||
int word = VL_BITWORD_I(rd);
|
||||
if (VL_UNLIKELY((int)rd>lbits)) {
|
||||
if (VL_UNLIKELY(rd>(IData)lbits)) {
|
||||
return ~0; // Spec says you can go outside the range of a array. Don't coredump if so.
|
||||
// We return all 1's as that's more likely to find bugs (?) than 0's.
|
||||
} else {
|
||||
|
||||
@@ -43,14 +43,14 @@ VK_CPPFLAGS_ALWAYS += \
|
||||
-DVL_PRINTF=printf \
|
||||
-DVM_TRACE=$(VM_TRACE) \
|
||||
-DVM_COVERAGE=$(VM_COVERAGE) \
|
||||
$(CFG_CXXFLAGS_NO_UNUSED) \
|
||||
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
VK_CPPFLAGS_WALL += -Wall \
|
||||
$(CFG_CXXFLAGS_NO_UNUSED) \
|
||||
-Werror
|
||||
endif
|
||||
|
||||
CPPFLAGS += -I. $(VK_CPPFLAGS_ALWAYS) $(VK_CPPFLAGS_WALL)
|
||||
CPPFLAGS += -I. $(VK_CPPFLAGS_WALL) $(VK_CPPFLAGS_ALWAYS)
|
||||
|
||||
VPATH += ..
|
||||
VPATH += $(VERILATOR_ROOT)/include
|
||||
@@ -195,6 +195,7 @@ debug-make::
|
||||
@echo VM_SUPPORT_SLOW: $(VM_SUPPORT_SLOW)
|
||||
@echo VM_GLOBAL_FAST: $(VM_GLOBAL_FAST)
|
||||
@echo VM_GLOBAL_SLOW: $(VM_GLOBAL_SLOW)
|
||||
@echo CPPFLAGS: $(CPPFLAGS)
|
||||
@echo
|
||||
|
||||
######################################################################
|
||||
|
||||
@@ -62,8 +62,9 @@ bool VerilatedDeserialize::readDiffers (const void* __restrict datap, size_t siz
|
||||
|
||||
VerilatedDeserialize& VerilatedDeserialize::readAssert (const void* __restrict datap, size_t size) {
|
||||
if (VL_UNLIKELY(readDiffers(datap,size))) {
|
||||
string fn = filename();
|
||||
string msg = (string)"Can't deserialize save-restore file as was made from different model";
|
||||
vl_fatal(filename().c_str(), 0, "", msg.c_str());
|
||||
vl_fatal(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
return *this; // For function chaining
|
||||
@@ -82,8 +83,9 @@ void VerilatedSerialize::header() {
|
||||
void VerilatedDeserialize::header() {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)))) {
|
||||
string fn = filename();
|
||||
string msg = (string)"Can't deserialize; file has wrong header signature";
|
||||
vl_fatal(filename().c_str(), 0, "", msg.c_str());
|
||||
vl_fatal(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
os.read(Verilated::serializedPtr(), Verilated::serializedSize());
|
||||
@@ -98,8 +100,9 @@ void VerilatedSerialize::trailer() {
|
||||
void VerilatedDeserialize::trailer() {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR)))) {
|
||||
string fn = filename();
|
||||
string msg = (string)"Can't deserialize; file has wrong end-of-file signature";
|
||||
vl_fatal(filename().c_str(), 0, "", msg.c_str());
|
||||
vl_fatal(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,8 +172,8 @@ void VerilatedVcd::makeNameMap() {
|
||||
// This comes from user instantiations with no name - IE Vtop("").
|
||||
bool nullScope = false;
|
||||
for (NameMap::iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
|
||||
const char* hiername = (*it).first.c_str();
|
||||
if (hiername[0] == '\t') nullScope=true;
|
||||
const string& hiername = it->first;
|
||||
if (hiername.size() >= 1 && hiername[0] == '\t') nullScope=true;
|
||||
}
|
||||
if (nullScope) {
|
||||
NameMap* newmapp = new NameMap;
|
||||
@@ -261,6 +261,20 @@ void VerilatedVcd::printTime (vluint64_t timeui) {
|
||||
printQuad(timeui);
|
||||
}
|
||||
|
||||
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);
|
||||
m_writep = m_wrBufp + (m_writep - oldbufp);
|
||||
m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
|
||||
delete oldbufp; oldbufp=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::bufferFlush () {
|
||||
// We add output data to m_writep.
|
||||
// When it gets nearly full we dump it using this routine which calls write()
|
||||
@@ -352,7 +366,8 @@ void VerilatedVcd::dumpHeader () {
|
||||
printStr("$date "); printStr(ctime(&time_str)); printStr(" $end\n");
|
||||
|
||||
printStr("$timescale ");
|
||||
printStr(doubleToTimescale(m_timeRes).c_str());
|
||||
const string& timeResStr = doubleToTimescale(m_timeRes);
|
||||
printStr(timeResStr.c_str());
|
||||
printStr(" $end\n");
|
||||
|
||||
makeNameMap();
|
||||
@@ -370,10 +385,11 @@ void VerilatedVcd::dumpHeader () {
|
||||
// Print the signal names
|
||||
const char* lastName = "";
|
||||
for (NameMap::iterator it=m_namemapp->begin(); it!=m_namemapp->end(); ++it) {
|
||||
const char* hiername = (*it).first.c_str();
|
||||
const char* decl = (*it).second.c_str();
|
||||
const string& hiernamestr = it->first;
|
||||
const 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;
|
||||
@@ -408,7 +424,7 @@ void VerilatedVcd::dumpHeader () {
|
||||
}
|
||||
|
||||
printIndent(0);
|
||||
printStr(decl);
|
||||
printStr(decl.c_str());
|
||||
}
|
||||
|
||||
while (m_modDepth>1) {
|
||||
@@ -442,6 +458,9 @@ void VerilatedVcd::declare (vluint32_t code, const char* name, const char* wirep
|
||||
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
|
||||
bufferResize(bits+1024);
|
||||
|
||||
// Save declaration info
|
||||
VerilatedVcdSig sig = VerilatedVcdSig(code, bits);
|
||||
m_sigs.push_back(sig);
|
||||
@@ -520,7 +539,7 @@ void VerilatedVcd::declDouble (vluint32_t code, const char* name, int arraynum
|
||||
|
||||
void VerilatedVcd::fullDouble (vluint32_t code, const double newval) {
|
||||
(*((double*)&m_sigs_oldvalp[code])) = newval;
|
||||
// Buffer can't overflow; we have at least bufferInsertSize() bytes (>>>16 bytes)
|
||||
// Buffer can't overflow before sprintf; we sized during declaration
|
||||
sprintf(m_writep, "r%.16g", newval);
|
||||
m_writep += strlen(m_writep);
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
@@ -528,7 +547,7 @@ void VerilatedVcd::fullDouble (vluint32_t code, const double newval) {
|
||||
}
|
||||
void VerilatedVcd::fullFloat (vluint32_t code, const float newval) {
|
||||
(*((float*)&m_sigs_oldvalp[code])) = newval;
|
||||
// Buffer can't overflow; we have at least bufferInsertSize() bytes (>>>16 bytes)
|
||||
// Buffer can't overflow before sprintf; we sized during declaration
|
||||
sprintf(m_writep, "r%.16g", (double)newval);
|
||||
m_writep += strlen(m_writep);
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
@@ -701,5 +720,5 @@ main() {
|
||||
|
||||
//********************************************************************
|
||||
// Local Variables:
|
||||
// compile-command: "mkdir -p ../test_dir && cd ../test_dir && g++ -DVERILATED_VCD_TEST ../src/verilated_vcd_c.cpp -o verilated_vcd_c && ./verilated_vcd_c && cat test.vcd"
|
||||
// compile-command: "mkdir -p ../test_dir && cd ../test_dir && c++ -DVERILATED_VCD_TEST ../src/verilated_vcd_c.cpp -o verilated_vcd_c && ./verilated_vcd_c && cat test.vcd"
|
||||
// End:
|
||||
|
||||
@@ -77,7 +77,9 @@ private:
|
||||
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
|
||||
|
||||
vluint32_t* m_sigs_oldvalp; ///< Pointer to old signal values
|
||||
@@ -87,13 +89,12 @@ private:
|
||||
NameMap* m_namemapp; ///< List of names for the header
|
||||
static vector<VerilatedVcd*> s_vcdVecp; ///< List of all created traces
|
||||
|
||||
inline static size_t bufferSize() { return 256*1024; } // See below for slack calculation
|
||||
inline static size_t bufferInsertSize() { return 16*1024; }
|
||||
void bufferResize(vluint64_t minsize);
|
||||
void bufferFlush();
|
||||
void bufferCheck() {
|
||||
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_wrBufp+(bufferSize()-bufferInsertSize())))) {
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
|
||||
bufferFlush();
|
||||
}
|
||||
}
|
||||
@@ -135,17 +136,19 @@ protected:
|
||||
public:
|
||||
// CREATORS
|
||||
VerilatedVcd () : m_isOpen(false), m_rolloverMB(0), m_modDepth(0), m_nextCode(1) {
|
||||
m_wrBufp = new char [bufferSize()];
|
||||
m_writep = m_wrBufp;
|
||||
m_namemapp = NULL;
|
||||
m_timeRes = m_timeUnit = 1e-9;
|
||||
m_timeLastDump = 0;
|
||||
m_sigs_oldvalp = NULL;
|
||||
m_evcd = false;
|
||||
m_scopeEscape = '.'; // Backward compatibility
|
||||
m_wroteBytes = 0;
|
||||
m_fd = 0;
|
||||
m_fullDump = true;
|
||||
m_wrChunkSize = 8*1024;
|
||||
m_wrBufp = new char [m_wrChunkSize*8];
|
||||
m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
|
||||
m_writep = m_wrBufp;
|
||||
m_wroteBytes = 0;
|
||||
}
|
||||
~VerilatedVcd();
|
||||
|
||||
|
||||
+22
-28
@@ -97,8 +97,8 @@ public:
|
||||
virtual const char* fullname() { return "<null>"; }
|
||||
virtual const char* defname() { return "<null>"; }
|
||||
virtual const vluint32_t type() { return 0; }
|
||||
virtual const vluint32_t size() { return 0; }
|
||||
virtual const VerilatedRange* rangep() { return 0; }
|
||||
virtual const vluint32_t size() const { return 0; }
|
||||
virtual const VerilatedRange* rangep() const { return NULL; }
|
||||
virtual vpiHandle dovpi_scan() { return 0; }
|
||||
};
|
||||
|
||||
@@ -136,7 +136,7 @@ public:
|
||||
|
||||
class VerilatedVpioRange : public VerilatedVpio {
|
||||
const VerilatedRange* m_range;
|
||||
bool m_iteration;
|
||||
vlsint32_t m_iteration;
|
||||
public:
|
||||
VerilatedVpioRange(const VerilatedRange* range) : m_range(range), m_iteration(0) {}
|
||||
virtual ~VerilatedVpioRange() {}
|
||||
@@ -182,7 +182,7 @@ class VerilatedVpioVar : public VerilatedVpio {
|
||||
protected:
|
||||
void* m_varDatap; // varp()->datap() adjusted for array entries
|
||||
vlsint32_t m_index;
|
||||
const VerilatedRange& get_range() {
|
||||
const VerilatedRange& get_range() const {
|
||||
// Determine number of dimensions and return outermost
|
||||
return (m_varp->dims()>1) ? m_varp->array() : m_varp->range();
|
||||
}
|
||||
@@ -208,8 +208,8 @@ public:
|
||||
if (varp()->vldir() != vpiNoDirection) return vpiPort;
|
||||
return (varp()->dims()>1) ? vpiMemory : vpiReg; /* but might be wire, logic */
|
||||
}
|
||||
virtual const vluint32_t size() { return get_range().elements(); }
|
||||
virtual const VerilatedRange* rangep() { return &get_range(); }
|
||||
virtual const vluint32_t size() const { return get_range().elements(); }
|
||||
virtual const VerilatedRange* rangep() const { return &get_range(); }
|
||||
virtual const char* name() { return m_varp->name(); }
|
||||
virtual const char* fullname() {
|
||||
VL_STATIC_OR_THREAD string out;
|
||||
@@ -237,8 +237,8 @@ public:
|
||||
virtual ~VerilatedVpioMemoryWord() {}
|
||||
static inline VerilatedVpioMemoryWord* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioMemoryWord*>((VerilatedVpio*)h); }
|
||||
virtual const vluint32_t type() { return vpiMemoryWord; }
|
||||
virtual const vluint32_t size() { return varp()->range().elements(); }
|
||||
virtual const VerilatedRange* rangep() { return &(varp()->range()); }
|
||||
virtual const vluint32_t size() const { return varp()->range().elements(); }
|
||||
virtual const VerilatedRange* rangep() const { return &(varp()->range()); }
|
||||
virtual const char* fullname() {
|
||||
VL_STATIC_OR_THREAD string out;
|
||||
char num[20]; sprintf(num,"%d",m_index);
|
||||
@@ -259,10 +259,11 @@ public:
|
||||
virtual const vluint32_t type() { return vpiIterator; }
|
||||
virtual vpiHandle dovpi_scan() {
|
||||
if (VL_LIKELY(m_scopep->varsp())) {
|
||||
if (VL_UNLIKELY(!m_started)) { m_it = m_scopep->varsp()->begin(); m_started=true; }
|
||||
else if (VL_UNLIKELY(m_it == m_scopep->varsp()->end())) return 0;
|
||||
VerilatedVarNameMap* varsp = m_scopep->varsp();
|
||||
if (VL_UNLIKELY(!m_started)) { m_it = varsp->begin(); m_started=true; }
|
||||
else if (VL_UNLIKELY(m_it == varsp->end())) return 0;
|
||||
else ++m_it;
|
||||
if (m_it == m_scopep->varsp()->end()) return 0;
|
||||
if (m_it == varsp->end()) return 0;
|
||||
return ((new VerilatedVpioVar(&(m_it->second), m_scopep))
|
||||
->castVpiHandle());
|
||||
} else {
|
||||
@@ -283,7 +284,7 @@ public:
|
||||
virtual ~VerilatedVpioMemoryWordIter() {}
|
||||
static inline VerilatedVpioMemoryWordIter* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioMemoryWordIter*>((VerilatedVpio*)h); }
|
||||
virtual const vluint32_t type() { return vpiIterator; }
|
||||
void iterationInc() { if (!(m_done = m_iteration == m_varp->array().left())) m_iteration+=m_direction; }
|
||||
void iterationInc() { if (!(m_done = (m_iteration == m_varp->array().left()))) m_iteration+=m_direction; }
|
||||
virtual vpiHandle dovpi_scan() {
|
||||
vpiHandle result;
|
||||
if (m_done) return 0;
|
||||
@@ -408,7 +409,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
for (set<VerilatedVpioVar*>::iterator it=update.begin(); it!=update.end(); it++ ) {
|
||||
for (set<VerilatedVpioVar*>::iterator it=update.begin(); it!=update.end(); ++it) {
|
||||
memcpy((*it)->prevDatap(), (*it)->varDatap(), (*it)->entSize());
|
||||
}
|
||||
}
|
||||
@@ -430,15 +431,10 @@ class VerilatedVpiError {
|
||||
t_vpi_error_info m_errorInfo;
|
||||
bool m_flag;
|
||||
char m_buff[VL_VPI_LINE_SIZE];
|
||||
void setError(PLI_BYTE8 *message, PLI_BYTE8 *file, PLI_INT32 line) {
|
||||
void setError(PLI_BYTE8 *message, PLI_BYTE8 *code, PLI_BYTE8 *file, PLI_INT32 line) {
|
||||
m_errorInfo.message = message;
|
||||
m_errorInfo.file = file;
|
||||
m_errorInfo.line = line;
|
||||
m_errorInfo.code = NULL;
|
||||
do_callbacks();
|
||||
}
|
||||
void setError(PLI_BYTE8 *message, PLI_BYTE8 *code, PLI_BYTE8 *file, PLI_INT32 line) {
|
||||
setError( message, file, line);
|
||||
m_errorInfo.code = code;
|
||||
do_callbacks();
|
||||
}
|
||||
@@ -454,6 +450,7 @@ class VerilatedVpiError {
|
||||
public:
|
||||
|
||||
VerilatedVpiError() : m_flag(false) {
|
||||
m_buff[0] = '\0';
|
||||
m_errorInfo.product = (PLI_BYTE8*)Verilated::productName();
|
||||
}
|
||||
~VerilatedVpiError() {}
|
||||
@@ -463,14 +460,11 @@ public:
|
||||
return this;
|
||||
}
|
||||
void setMessage(string file, PLI_INT32 line, string message, ...) {
|
||||
static VL_THREAD string filehold;
|
||||
_VL_VPI_ERROR_SET;
|
||||
m_errorInfo.state = vpiPLI;
|
||||
setError((PLI_BYTE8*)m_buff, (PLI_BYTE8*)file.c_str(), line);
|
||||
}
|
||||
void setMessage(PLI_BYTE8 *code, PLI_BYTE8 *file, PLI_INT32 line, string message, ...) {
|
||||
_VL_VPI_ERROR_SET;
|
||||
m_errorInfo.state = vpiPLI;
|
||||
setError((PLI_BYTE8*)message.c_str(), code, file, line);
|
||||
filehold = file;
|
||||
setError((PLI_BYTE8*)m_buff, NULL, (PLI_BYTE8*)filehold.c_str(), line);
|
||||
}
|
||||
p_vpi_error_info getError() {
|
||||
if (m_flag) return &m_errorInfo;
|
||||
@@ -925,9 +919,9 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
value_p->value.str = outStr;
|
||||
switch (vop->varp()->vltype()) {
|
||||
// outStrSz does not include NULL termination so add one
|
||||
case VLVT_UINT8 : snprintf(outStr, outStrSz+1, "%hhu", (unsigned int)*((CData*)(vop->varDatap()))); return;
|
||||
case VLVT_UINT16: snprintf(outStr, outStrSz+1, "%hu", (unsigned int)*((SData*)(vop->varDatap()))); return;
|
||||
case VLVT_UINT32: snprintf(outStr, outStrSz+1, "%u", (unsigned int)*((IData*)(vop->varDatap()))); return;
|
||||
case VLVT_UINT8 : snprintf(outStr, outStrSz+1, "%hhu", (unsigned char )*((CData*)(vop->varDatap()))); return;
|
||||
case VLVT_UINT16: snprintf(outStr, outStrSz+1, "%hu", (unsigned short)*((SData*)(vop->varDatap()))); return;
|
||||
case VLVT_UINT32: snprintf(outStr, outStrSz+1, "%u", (unsigned int )*((IData*)(vop->varDatap()))); return;
|
||||
case VLVT_UINT64: snprintf(outStr, outStrSz+1, "%llu", (unsigned long long)*((QData*)(vop->varDatap()))); return;
|
||||
default:
|
||||
strcpy(outStr, "-1");
|
||||
|
||||
@@ -95,6 +95,16 @@
|
||||
// This is not necessarily the same as #UL, depending on what the IData typedef is.
|
||||
#define VL_UL(c) ((IData)(c##UL)) ///< Add appropriate suffix to 32-bit constant
|
||||
|
||||
//=========================================================================
|
||||
// C++-2011
|
||||
|
||||
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||
# define VL_HAS_UNIQUE_PTR
|
||||
# define VL_UNIQUE_PTR unique_ptr
|
||||
#else
|
||||
# define VL_UNIQUE_PTR auto_ptr
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
// Warning disabled
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ typedef signed __int8 int8_t;
|
||||
#include <stdint.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <stdint.h>
|
||||
#elif defined(__linux)
|
||||
#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#include <inttypes.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -37,7 +37,7 @@ typedef signed __int32 int32_t;
|
||||
typedef signed __int8 int8_t;
|
||||
#elif defined(__MINGW32__)
|
||||
#include <stdint.h>
|
||||
#elif defined(__linux)
|
||||
#elif defined(__linux) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#include <inttypes.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
|
||||
+47
-16
@@ -40,7 +40,7 @@ Verilator then performs many additional edits and optimizations on the
|
||||
hierarchical design. This includes coverage, assertions, X elimination,
|
||||
inlining, constant propagation, and dead code elimination.
|
||||
|
||||
References in the design are then psudo-flattened. Each module's variables
|
||||
References in the design are then pseudo-flattened. Each module's variables
|
||||
and functions get "Scope" references. A scope reference is an occurrence of
|
||||
that un-flattened variable in the flattened hierarchy. A module that occurs
|
||||
only once in the hierarchy will have a single scope and single VarScope for
|
||||
@@ -49,7 +49,7 @@ occurrence, and two VarScopes for each variable. This allows optimizations
|
||||
to proceed across the flattened design, while still preserving the
|
||||
hierarchy.
|
||||
|
||||
Additional edits and optimizations proceed on the psudo-flat design. These
|
||||
Additional edits and optimizations proceed on the pseudo-flat design. These
|
||||
include module references, function inlining, loop unrolling, variable
|
||||
lifetime analysis, lookup table creation, always splitting, and logic gate
|
||||
simplifications (pushing inverters, etc).
|
||||
@@ -80,8 +80,8 @@ classes).
|
||||
Each C<AstNode> has pointers to up to four children, accessed by the
|
||||
C<op1p> through C<op4p> methods. These methods are then abstracted in a
|
||||
specific Ast* node class to a more specific name. For example with the
|
||||
C<AstIf> node (for C<if> statements), C<ifsp> calls C<op1p> to give the
|
||||
pointer to the AST for the "then" block, while C<elsesp> calls C<op2p> to
|
||||
C<AstIf> node (for C<if> statements), C<ifsp> calls C<op2p> to give the
|
||||
pointer to the AST for the "then" block, while C<elsesp> calls C<op3p> to
|
||||
give the pointer to the AST for the "else" block, or NULL if there is not
|
||||
one.
|
||||
|
||||
@@ -97,7 +97,7 @@ are at the top of the tree.
|
||||
By convention, each function/method uses the variable C<nodep> as a pointer
|
||||
to the C<AstNode> currently being processed.
|
||||
|
||||
=item C<AstNVistor>
|
||||
=item C<AstNVisitor>
|
||||
|
||||
The passes are implemented by AST visitor classes (see L</Visitor
|
||||
Functions>). These are implemented by subclasses of the abstract class,
|
||||
@@ -119,7 +119,7 @@ C<fanout>, C<color> and C<rank>, which may be used in algorithms for ordering
|
||||
the graph. A generic C<user>/C<userp> member variable is also provided.
|
||||
|
||||
Virtual methods are provided to specify the name, color, shape and style to be
|
||||
used in dot output. Typically users provided derived classes from
|
||||
used in dot output. Typically users provide derived classes from
|
||||
C<V3GraphVertex> which will reimplement these methods.
|
||||
|
||||
Iterators are provided to access in and out edges. Typically these are used in
|
||||
@@ -219,7 +219,7 @@ and optimization passes. This allows separation of the pass algorithm from
|
||||
the AST on which it operates. Wikipedia provides an introduction to the
|
||||
concept at L<http://en.wikipedia.org/wiki/Visitor_pattern>.
|
||||
|
||||
As noted above, all visitors are derived classes of C<AstNvisitor>. All
|
||||
As noted above, all visitors are derived classes of C<AstNVisitor>. All
|
||||
derived classes of C<AstNode> implement the C<accept> method, which takes
|
||||
as argument a reference to an instance or a C<AstNVisitor> derived class
|
||||
and applies the visit method of the C<AstNVisitor> to the invoking AstNode
|
||||
@@ -262,7 +262,7 @@ exiting the lower for will lose the upper for's setting.
|
||||
User attributes. Each C<AstNode> (B<Note.> The AST node, not the visitor)
|
||||
has five user attributes, which may be accessed as an integer using the
|
||||
C<user1()> through C<user5()> methods, or as a pointer (of type
|
||||
C<AstNuser>) using the C<user1p()> through C<user5p()> methods (a common
|
||||
C<AstNUser>) using the C<user1p()> through C<user5p()> methods (a common
|
||||
technique lifted from graph traversal packages).
|
||||
|
||||
A visitor first clears the one it wants to use by calling
|
||||
@@ -290,7 +290,7 @@ module.
|
||||
|
||||
Parameters can be passed between the visitors in close to the "normal"
|
||||
function caller to callee way. This is the second C<vup> parameter of type
|
||||
C<AstNuser> that is ignored on most of the visitor functions. V3Width does
|
||||
C<AstNUser> that is ignored on most of the visitor functions. V3Width does
|
||||
this, but it proved more messy than the above and is deprecated. (V3Width
|
||||
was nearly the first module written. Someday this scheme may be removed,
|
||||
as it slows the program down to have to pass vup everywhere.)
|
||||
@@ -301,10 +301,10 @@ as it slows the program down to have to pass vup everywhere.)
|
||||
|
||||
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
|
||||
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 no deprecated and should be used for new visitor
|
||||
classes.
|
||||
Functions>, but its use is now deprecated and should I<not> be used for new
|
||||
visitor classes.
|
||||
|
||||
=over 4
|
||||
|
||||
@@ -341,6 +341,37 @@ C<op4p> in turn.
|
||||
|
||||
=back
|
||||
|
||||
=head3 Caution on Using Iterators When Child Changes
|
||||
|
||||
Visitors often replace one node with another node; V3Width and V3Const are
|
||||
major examples. A visitor which is the parent of such a replacement needs
|
||||
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
|
||||
// nodep->lhsp() is 0x5678400
|
||||
nodep->lhsp()->iterateAndNext(...);
|
||||
|
||||
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
|
||||
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
|
||||
// nodep->lhsp() is 0x5678400, lp is 0x1234000
|
||||
lp->iterateAndNext(...);
|
||||
|
||||
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
|
||||
dangles are avoided. Another alternative used in special cases mostly in
|
||||
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()
|
||||
|
||||
=head2 Identifying derived classes
|
||||
|
||||
A common requirement is to identify the specific C<AstNode> class we are
|
||||
@@ -372,7 +403,7 @@ Verilator primary manual.
|
||||
It is important to add tests for failures as well as success (for example to
|
||||
check that an error message is correctly triggered).
|
||||
|
||||
Tests that fail should by convenition have the suffix C<_bad> in their name,
|
||||
Tests that fail should by convention have the suffix C<_bad> in their name,
|
||||
and include C<fails =E<gt> 1> in either their C<compile> or C<execute> step as
|
||||
appropriate.
|
||||
|
||||
@@ -388,7 +419,7 @@ For convenience, a summary of the most commonly used features is provided
|
||||
here. All drivers require a call to C<compile> subroutine to compile the
|
||||
test. For run-time tests, this is followed by a call to the C<execute>
|
||||
subroutine. Both of these functions can optionally be provided with a hash
|
||||
table as argument specifying additonal options.
|
||||
table as argument specifying additional options.
|
||||
|
||||
The test driver assumes by default that the source Verilog file name
|
||||
matches the PERL driver name. So a test whose driver is C<t/t_mytest.pl>
|
||||
@@ -608,8 +639,8 @@ Shows the value of the node's user1p...user5p, if non-NULL.
|
||||
Many nodes have an explicit data type. "@dt=0x..." indicates the address
|
||||
of the data type (AstNodeDType) this node uses.
|
||||
|
||||
If a data type is present and is numberic, it then prints the width of the
|
||||
item. This field is a squence of flag characters and width data as follows:
|
||||
If a data type is present and is numeric, it then prints the width of the
|
||||
item. This field is a sequence of flag characters and width data as follows:
|
||||
|
||||
C<s> if the node is signed.
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ use strict;
|
||||
#======================================================================
|
||||
# main
|
||||
|
||||
eval `modulecmd perl add synopsys-vcs`;
|
||||
eval `modulecmd perl add synopsys-vcs_mx`;
|
||||
exec('vcs',@ARGV);
|
||||
|
||||
#######################################################################
|
||||
|
||||
@@ -326,12 +326,10 @@ private:
|
||||
bool sequent = m_itemSequent;
|
||||
|
||||
if (!combo && !sequent) combo=true; // If no list, Verilog 2000: always @ (*)
|
||||
#ifndef NEW_ORDERING
|
||||
if (combo && sequent) {
|
||||
nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity (no edge) sensitive activity list");
|
||||
sequent = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
AstActive* wantactivep = NULL;
|
||||
if (combo && !sequent) {
|
||||
|
||||
+83
-18
@@ -56,15 +56,18 @@ private:
|
||||
+":"+cvtToStr(nodep->fileline()->lineno())
|
||||
+": Assertion failed in %m"
|
||||
+((message != "")?": ":"")+message
|
||||
+"\\n");
|
||||
+"\n");
|
||||
}
|
||||
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
|
||||
nodep->displayType(AstDisplayType::DT_WRITE);
|
||||
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
|
||||
AstNode* timesp = nodep->fmtp()->exprsp(); if (timesp) timesp->unlinkFrBack();
|
||||
// cppcheck-suppress nullPointer
|
||||
timesp = timesp->addNext(new AstTime(nodep->fileline()));
|
||||
nodep->fmtp()->exprsp(timesp);
|
||||
AstNode* timenewp = new AstTime(nodep->fileline());
|
||||
if (AstNode* timesp = nodep->fmtp()->exprsp()) {
|
||||
timesp->unlinkFrBackWithNext();
|
||||
timenewp->addNext(timesp);
|
||||
}
|
||||
nodep->fmtp()->exprsp(timenewp);
|
||||
if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
|
||||
nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline()));
|
||||
}
|
||||
@@ -73,25 +76,31 @@ private:
|
||||
AstNode* newIfAssertOn(AstNode* nodep) {
|
||||
// Add a internal if to check assertions are on.
|
||||
// Don't make this a AND term, as it's unlikely to need to test this.
|
||||
return new AstIf (nodep->fileline(),
|
||||
// If assertions are off, have constant propagation rip them out later
|
||||
// This allows syntax errors and such to be detected normally.
|
||||
(v3Global.opt.assertOn()
|
||||
? (AstNode*)(new AstCMath(nodep->fileline(), "Verilated::assertOn()", 1))
|
||||
: (AstNode*)(new AstConst(nodep->fileline(), AstConst::LogicFalse()))),
|
||||
nodep, NULL);
|
||||
AstNode* newp
|
||||
= new AstIf (nodep->fileline(),
|
||||
// If assertions are off, have constant propagation rip them out later
|
||||
// This allows syntax errors and such to be detected normally.
|
||||
(v3Global.opt.assertOn()
|
||||
? (AstNode*)(new AstCMath(nodep->fileline(), "Verilated::assertOn()", 1))
|
||||
: (AstNode*)(new AstConst(nodep->fileline(), AstConst::LogicFalse()))),
|
||||
nodep, NULL);
|
||||
newp->user1(true); // Don't assert/cover this if
|
||||
return newp;
|
||||
}
|
||||
|
||||
AstNode* newIfCoverageOn(AstNode* nodep) {
|
||||
// Add a internal if to check coverage is on
|
||||
// Don't make this a AND term, as it's unlikely to need to test this.
|
||||
return new AstIf (nodep->fileline(),
|
||||
// If assertions are off, have constant propagation rip them out later
|
||||
// This allows syntax errors and such to be detected normally.
|
||||
(v3Global.opt.coverage()
|
||||
? (AstNode*)(new AstConst(nodep->fileline(), AstConst::LogicTrue()))
|
||||
: (AstNode*)(new AstConst(nodep->fileline(), AstConst::LogicFalse()))),
|
||||
nodep, NULL);
|
||||
AstNode* newp
|
||||
= new AstIf (nodep->fileline(),
|
||||
// If assertions are off, have constant propagation rip them out later
|
||||
// This allows syntax errors and such to be detected normally.
|
||||
(v3Global.opt.coverage()
|
||||
? (AstNode*)(new AstConst(nodep->fileline(), AstConst::LogicTrue()))
|
||||
: (AstNode*)(new AstConst(nodep->fileline(), AstConst::LogicFalse()))),
|
||||
nodep, NULL);
|
||||
newp->user1(true); // Don't assert/cover this if
|
||||
return newp;
|
||||
}
|
||||
|
||||
AstNode* newFireAssert(AstNode* nodep, const string& message) {
|
||||
@@ -174,6 +183,62 @@ private:
|
||||
// Bye
|
||||
pushDeletep(nodep); nodep=NULL;
|
||||
}
|
||||
|
||||
virtual void visit(AstIf* nodep, AstNUser*) {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||
AstNodeIf* ifp = nodep;
|
||||
AstNode* propp = NULL;
|
||||
bool hasDefaultElse = false;
|
||||
do {
|
||||
// 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);
|
||||
|
||||
// Recurse into the true case.
|
||||
ifp->ifsp()->iterateAndNext(*this);
|
||||
|
||||
// If the last else is not an else if, recurse into that too.
|
||||
if (ifp->elsesp() && !nextifp) {
|
||||
ifp->elsesp()->iterateAndNext(*this);
|
||||
}
|
||||
|
||||
// Build a bitmask of the true predicates
|
||||
AstNode* predp = ifp->condp()->cloneTree(false);
|
||||
if (propp) {
|
||||
propp = new AstConcat(nodep->fileline(), predp, propp);
|
||||
} else {
|
||||
propp = predp;
|
||||
}
|
||||
|
||||
// Record if this ends with an 'else' that does not have an if
|
||||
if (ifp->elsesp() && !nextifp) {
|
||||
hasDefaultElse = true;
|
||||
}
|
||||
|
||||
ifp = nextifp;
|
||||
} while (ifp);
|
||||
|
||||
AstNode *newifp = nodep->cloneTree(false);
|
||||
bool allow_none = nodep->unique0Pragma();
|
||||
|
||||
// Note: if this ends with an 'else', then we don't need to validate that one of the
|
||||
// predicates evaluates to true.
|
||||
AstNode* ohot = ((allow_none || hasDefaultElse)
|
||||
? (new AstOneHot0(nodep->fileline(), propp))->castNode()
|
||||
: (new AstOneHot (nodep->fileline(), propp))->castNode());
|
||||
AstIf* checkifp = new AstIf (nodep->fileline(),
|
||||
new AstLogNot (nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "'unique if' statement violated"),
|
||||
newifp);
|
||||
checkifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
nodep->replaceWith(checkifp);
|
||||
pushDeletep(nodep);
|
||||
} else {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS //========== Case assertions
|
||||
virtual void visit(AstCase* nodep, AstNUser*) {
|
||||
|
||||
+35
-18
@@ -239,6 +239,9 @@ inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next)
|
||||
//if (debug()) cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<": "<<(void*)this<<" <e"<<AstNode::s_editCntGbl<<">"<<endl;
|
||||
//if (debug()) {
|
||||
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
|
||||
// // Commenting out the section below may crash, as the tree state
|
||||
// // between edits is not always consistent for printing
|
||||
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
|
||||
// v3Global.rootp()->dumpTree(cout,"-treeChange: ");
|
||||
// if (next||1) this->dumpTreeAndNext(cout, prefix);
|
||||
// else this->dumpTree(cout, prefix);
|
||||
@@ -561,7 +564,7 @@ void AstNode::relink(AstNRelinker* linkerp) {
|
||||
if (linkerp->m_iterpp) {
|
||||
// If we're iterating over a next() link, we need to follow links off the
|
||||
// NEW node. Thus we pass iteration information via a pointer in the node.
|
||||
// This adds a unfortunate 4 bytes to every AstNode, but is faster than passing
|
||||
// This adds a unfortunate hot 8 bytes to every AstNode, but is faster than passing
|
||||
// across every function.
|
||||
// If anyone has a cleaner way, I'd be grateful.
|
||||
*(linkerp->m_iterpp) = newp;
|
||||
@@ -750,6 +753,20 @@ void AstNode::iterateChildren(AstNVisitor& v, AstNUser* vup) {
|
||||
if (m_op4p) m_op4p->iterateAndNext(v, vup);
|
||||
}
|
||||
|
||||
void AstNode::iterateChildrenConst(AstNVisitor& v, AstNUser* vup) {
|
||||
// This is a very hot function
|
||||
if (!this) return;
|
||||
ASTNODE_PREFETCH(m_op1p);
|
||||
ASTNODE_PREFETCH(m_op2p);
|
||||
ASTNODE_PREFETCH(m_op3p);
|
||||
ASTNODE_PREFETCH(m_op4p);
|
||||
// if () not needed since iterateAndNext accepts null this, but faster with it.
|
||||
if (m_op1p) m_op1p->iterateAndNextConst(v, vup);
|
||||
if (m_op2p) m_op2p->iterateAndNextConst(v, vup);
|
||||
if (m_op3p) m_op3p->iterateAndNextConst(v, vup);
|
||||
if (m_op4p) m_op4p->iterateAndNextConst(v, vup);
|
||||
}
|
||||
|
||||
void AstNode::iterateAndNext(AstNVisitor& v, AstNUser* vup) {
|
||||
// This is a very hot function
|
||||
// IMPORTANT: If you replace a node that's the target of this iterator,
|
||||
@@ -764,16 +781,18 @@ void AstNode::iterateAndNext(AstNVisitor& v, AstNUser* vup) {
|
||||
while (nodep) {
|
||||
AstNode* niterp = nodep; // This address may get stomped via m_iterpp if the node is edited
|
||||
ASTNODE_PREFETCH(nodep->m_nextp);
|
||||
// Desirable check, but many places where multiple iterations are OK
|
||||
//if (VL_UNLIKELY(niterp->m_iterpp)) niterp->v3fatalSrc("IterateAndNext under iterateAndNext may miss edits");
|
||||
// cppcheck-suppress nullPointer
|
||||
niterp->m_iterpp = &niterp;
|
||||
niterp->accept(v, vup);
|
||||
// accept may do a replaceNode and change niterp on us...
|
||||
//if (niterp != nodep) UINFO(1,"iterateAndNext edited "<<(void*)nodep<<" now into "<<(void*)niterp<<endl); // niterp maybe NULL, so need cast
|
||||
if (!niterp) return;
|
||||
if (!niterp) return; // Perhaps node deleted inside accept
|
||||
niterp->m_iterpp = NULL;
|
||||
if (VL_UNLIKELY(niterp!=nodep)) { // Edited it
|
||||
if (VL_UNLIKELY(niterp!=nodep)) { // Edited node inside accept
|
||||
nodep = niterp;
|
||||
} else { // Same node, just loop
|
||||
} else { // Unchanged node, just continue loop
|
||||
nodep = niterp->m_nextp;
|
||||
}
|
||||
}
|
||||
@@ -799,11 +818,12 @@ void AstNode::iterateChildrenBackwards(AstNVisitor& v, AstNUser* vup) {
|
||||
this->op4p()->iterateListBackwards(v,vup);
|
||||
}
|
||||
|
||||
void AstNode::iterateAndNextIgnoreEdit(AstNVisitor& v, AstNUser* vup) {
|
||||
void AstNode::iterateAndNextConst(AstNVisitor& v, AstNUser* vup) {
|
||||
// Keep following the current list even if edits change it
|
||||
if (!this) return;
|
||||
for (AstNode* nodep=this; nodep; ) {
|
||||
AstNode* nnextp = nodep->m_nextp;
|
||||
ASTNODE_PREFETCH(nnextp);
|
||||
nodep->accept(v, vup);
|
||||
nodep = nnextp;
|
||||
}
|
||||
@@ -864,24 +884,21 @@ void AstNode::cloneRelinkTree() {
|
||||
//======================================================================
|
||||
// Comparison
|
||||
|
||||
bool AstNode::sameTree(AstNode* node2p) {
|
||||
return sameTreeIter(node2p, true);
|
||||
}
|
||||
|
||||
bool AstNode::sameTreeIter(AstNode* node2p, bool ignNext) {
|
||||
bool AstNode::sameTreeIter(AstNode* node2p, bool ignNext, bool gateOnly) {
|
||||
// Return true if the two trees are identical
|
||||
if (this==NULL && node2p==NULL) return true;
|
||||
if (this==NULL || node2p==NULL) return false;
|
||||
if (this->type() != node2p->type()
|
||||
|| this->dtypep() != node2p->dtypep()
|
||||
|| !this->same(node2p)) {
|
||||
|| !this->same(node2p)
|
||||
|| (gateOnly && !this->isGateOptimizable())) {
|
||||
return false;
|
||||
}
|
||||
return (this->op1p()->sameTreeIter(node2p->op1p(),false)
|
||||
&& this->op2p()->sameTreeIter(node2p->op2p(),false)
|
||||
&& this->op3p()->sameTreeIter(node2p->op3p(),false)
|
||||
&& this->op4p()->sameTreeIter(node2p->op4p(),false)
|
||||
&& (ignNext || this->nextp()->sameTreeIter(node2p->nextp(),false))
|
||||
return (this->op1p()->sameTreeIter(node2p->op1p(),false,gateOnly)
|
||||
&& this->op2p()->sameTreeIter(node2p->op2p(),false,gateOnly)
|
||||
&& this->op3p()->sameTreeIter(node2p->op3p(),false,gateOnly)
|
||||
&& this->op4p()->sameTreeIter(node2p->op4p(),false,gateOnly)
|
||||
&& (ignNext || this->nextp()->sameTreeIter(node2p->nextp(),false,gateOnly))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -895,8 +912,8 @@ ostream& operator<<(ostream& os, V3Hash rhs) {
|
||||
|
||||
V3Hash::V3Hash(const string& name) {
|
||||
uint32_t val = 0;
|
||||
for (const char* c=name.c_str(); *c; c++) {
|
||||
val = val*31 + *c;
|
||||
for (string::const_iterator it = name.begin(); it!=name.end(); ++it) {
|
||||
val = val*31 + *it;
|
||||
}
|
||||
setBoth(1,val);
|
||||
}
|
||||
|
||||
+83
-37
@@ -92,6 +92,8 @@ public:
|
||||
else if (signst==signedst_SIGNED) m_e=SIGNED;
|
||||
else m_e=NOSIGN;
|
||||
}
|
||||
static inline AstNumeric fromBool (bool isSigned) { // Factory method
|
||||
return isSigned ? AstNumeric(SIGNED) : AstNumeric(UNSIGNED); }
|
||||
explicit inline AstNumeric (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
inline bool isSigned() const { return m_e==SIGNED; }
|
||||
@@ -234,6 +236,8 @@ public:
|
||||
DIM_SIZE, // V3Width processes
|
||||
DIM_UNPK_DIMENSIONS, // V3Width converts to constant
|
||||
//
|
||||
DT_PUBLIC, // V3LinkParse moves to AstTypedef::attrPublic
|
||||
//
|
||||
MEMBER_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes
|
||||
//
|
||||
VAR_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes
|
||||
@@ -253,6 +257,7 @@ public:
|
||||
"%E-AT",
|
||||
"DIM_BITS", "DIM_DIMENSIONS", "DIM_HIGH", "DIM_INCREMENT", "DIM_LEFT",
|
||||
"DIM_LOW", "DIM_RIGHT", "DIM_SIZE", "DIM_UNPK_DIMENSIONS",
|
||||
"DT_PUBLIC",
|
||||
"MEMBER_BASE",
|
||||
"VAR_BASE", "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC",
|
||||
"VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD","VAR_PUBLIC_FLAT_RW",
|
||||
@@ -496,22 +501,23 @@ public:
|
||||
|
||||
//######################################################################
|
||||
|
||||
class AstCaseType {
|
||||
class VCaseType {
|
||||
public:
|
||||
enum en {
|
||||
CT_CASE,
|
||||
CT_CASEX,
|
||||
CT_CASEZ
|
||||
CT_CASEZ,
|
||||
CT_CASEINSIDE
|
||||
};
|
||||
enum en m_e;
|
||||
inline AstCaseType () : m_e(CT_CASE) {}
|
||||
inline AstCaseType (en _e) : m_e(_e) {}
|
||||
explicit inline AstCaseType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
inline VCaseType () : m_e(CT_CASE) {}
|
||||
inline VCaseType (en _e) : m_e(_e) {}
|
||||
explicit inline VCaseType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
};
|
||||
inline bool operator== (AstCaseType lhs, AstCaseType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstCaseType lhs, AstCaseType::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstCaseType::en lhs, AstCaseType rhs) { return (lhs == rhs.m_e); }
|
||||
inline bool operator== (VCaseType lhs, VCaseType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (VCaseType lhs, VCaseType::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (VCaseType::en lhs, VCaseType rhs) { return (lhs == rhs.m_e); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -605,13 +611,16 @@ struct VNumRange {
|
||||
int lo() const { return m_lo; }
|
||||
int left() const { return littleEndian()?lo():hi(); } // How to show a declaration
|
||||
int right() const { return littleEndian()?hi():lo(); }
|
||||
int leftToRightInc() const { return littleEndian()?1:-1; }
|
||||
int elements() const { return hi()-lo()+1; }
|
||||
bool ranged() const { return m_ranged; }
|
||||
bool littleEndian() const { return m_littleEndian; }
|
||||
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]"; }
|
||||
};
|
||||
inline ostream& operator<<(ostream& os, VNumRange rhs) { rhs.dump(os); return os; }
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -926,7 +935,7 @@ class AstNode {
|
||||
AstNode* cloneTreeIterList();
|
||||
void checkTreeIter(AstNode* backp);
|
||||
void checkTreeIterList(AstNode* backp);
|
||||
bool sameTreeIter(AstNode* node2p, bool ignNext);
|
||||
bool sameTreeIter(AstNode* node2p, bool ignNext, bool gateOnly);
|
||||
void deleteTreeIter();
|
||||
void deleteNode();
|
||||
static void relinkOneLink(AstNode*& pointpr, AstNode* newp);
|
||||
@@ -1013,7 +1022,8 @@ public:
|
||||
static string encodeNumber(vlsint64_t numin); // Encode number into internal C representation
|
||||
static string vcdName(const string& namein); // Name for printing out to vcd files
|
||||
string prettyName() const { return prettyName(name()); }
|
||||
string prettyTypeName() const; // "VARREF name" for error messages
|
||||
string prettyTypeName() const; // "VARREF" for error messages
|
||||
virtual string prettyOperatorName() const { return "operator "+prettyTypeName(); }
|
||||
FileLine* fileline() const { return m_fileline; }
|
||||
void fileline(FileLine* fl) { m_fileline=fl; }
|
||||
bool width1() const;
|
||||
@@ -1151,6 +1161,7 @@ public:
|
||||
// METHODS - Iterate on a tree
|
||||
AstNode* cloneTree(bool cloneNextLink);
|
||||
bool sameTree(AstNode* node2p); // Does tree of this == node2p?
|
||||
bool sameGateTree(AstNode* node2p); // Does tree of this == node2p?, not allowing non-isGateOptimizable
|
||||
void deleteTree(); // Always deletes the next link
|
||||
void checkTree(); // User Interface version
|
||||
void checkIter() const;
|
||||
@@ -1184,9 +1195,11 @@ public:
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) = 0;
|
||||
void iterate(AstNVisitor& v, AstNUser* vup=NULL) { this->accept(v,vup); } // Does this; excludes following this->next
|
||||
void iterateAndNext(AstNVisitor& v, AstNUser* vup=NULL);
|
||||
void iterateAndNextIgnoreEdit(AstNVisitor& v, AstNUser* vup=NULL);
|
||||
void iterateAndNextConst(AstNVisitor& v, AstNUser* vup=NULL);
|
||||
void iterateAndNextIgnoreEdit(AstNVisitor& v, AstNUser* vup=NULL) { iterateAndNextConst(v, vup); }
|
||||
void iterateChildren(AstNVisitor& v, AstNUser* vup=NULL); // Excludes following this->next
|
||||
void iterateChildrenBackwards(AstNVisitor& v, AstNUser* vup=NULL); // Excludes following this->next
|
||||
void iterateChildrenConst(AstNVisitor& v, AstNUser* vup=NULL); // Excludes following this->next
|
||||
AstNode* acceptSubtreeReturnEdits(AstNVisitor& v, AstNUser* vup=NULL); // Return edited nodep; see comments in V3Ast.cpp
|
||||
|
||||
// CONVERSION
|
||||
@@ -1207,8 +1220,9 @@ inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
|
||||
virtual ~Ast ##name() {} \
|
||||
Ast ##name * cloneTree(bool cloneNext) { return AstNode::cloneTree(cloneNext)->cast ##name(); }
|
||||
|
||||
struct AstNodeMath : public AstNode {
|
||||
class AstNodeMath : public AstNode {
|
||||
// Math -- anything that's part of an expression tree
|
||||
public:
|
||||
AstNodeMath(FileLine* fl)
|
||||
: AstNode(fl) {}
|
||||
ASTNODE_BASE_FUNCS(NodeMath)
|
||||
@@ -1223,8 +1237,9 @@ struct AstNodeMath : public AstNode {
|
||||
bool isOpaque() { return castCvtPackString()!=NULL; }
|
||||
};
|
||||
|
||||
struct AstNodeTermop : public AstNodeMath {
|
||||
class AstNodeTermop : public AstNodeMath {
|
||||
// Terminal operator -- a operator with no "inputs"
|
||||
public:
|
||||
AstNodeTermop(FileLine* fl)
|
||||
: AstNodeMath(fl) {}
|
||||
ASTNODE_BASE_FUNCS(NodeTermop)
|
||||
@@ -1234,8 +1249,9 @@ struct AstNodeTermop : public AstNodeMath {
|
||||
void iterateChildren(AstNVisitor& v, AstNUser* vup=NULL) { }
|
||||
};
|
||||
|
||||
struct AstNodeUniop : public AstNodeMath {
|
||||
class AstNodeUniop : public AstNodeMath {
|
||||
// Unary math
|
||||
public:
|
||||
AstNodeUniop(FileLine* fl, AstNode* lhsp)
|
||||
: AstNodeMath(fl) {
|
||||
dtypeFrom(lhsp);
|
||||
@@ -1254,8 +1270,9 @@ struct AstNodeUniop : public AstNodeMath {
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
};
|
||||
|
||||
struct AstNodeBiop : public AstNodeMath {
|
||||
class AstNodeBiop : public AstNodeMath {
|
||||
// Binary math
|
||||
public:
|
||||
AstNodeBiop(FileLine* fl, AstNode* lhs, AstNode* rhs)
|
||||
: AstNodeMath(fl) {
|
||||
setOp1p(lhs); setOp2p(rhs); }
|
||||
@@ -1277,8 +1294,9 @@ struct AstNodeBiop : public AstNodeMath {
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
};
|
||||
|
||||
struct AstNodeTriop : public AstNodeMath {
|
||||
class AstNodeTriop : public AstNodeMath {
|
||||
// Trinary math
|
||||
public:
|
||||
AstNodeTriop(FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths)
|
||||
: AstNodeMath(fl) {
|
||||
setOp1p(lhs); setOp2p(rhs); setOp3p(ths); }
|
||||
@@ -1302,20 +1320,23 @@ struct AstNodeTriop : public AstNodeMath {
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
};
|
||||
|
||||
struct AstNodeBiCom : public AstNodeBiop {
|
||||
class AstNodeBiCom : public AstNodeBiop {
|
||||
// Binary math with commutative properties
|
||||
public:
|
||||
AstNodeBiCom(FileLine* fl, AstNode* lhs, AstNode* rhs)
|
||||
: AstNodeBiop(fl, lhs, rhs) {}
|
||||
ASTNODE_BASE_FUNCS(NodeBiCom)
|
||||
};
|
||||
|
||||
struct AstNodeBiComAsv : public AstNodeBiCom {
|
||||
class AstNodeBiComAsv : public AstNodeBiCom {
|
||||
// Binary math with commutative & associative properties
|
||||
public:
|
||||
AstNodeBiComAsv(FileLine* fl, AstNode* lhs, AstNode* rhs)
|
||||
: AstNodeBiCom(fl, lhs, rhs) {}
|
||||
ASTNODE_BASE_FUNCS(NodeBiComAsv)
|
||||
};
|
||||
struct AstNodeCond : public AstNodeTriop {
|
||||
class AstNodeCond : public AstNodeTriop {
|
||||
public:
|
||||
AstNodeCond(FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p)
|
||||
: AstNodeTriop(fl, condp, expr1p, expr2p) {
|
||||
if (expr1p) dtypeFrom(expr1p);
|
||||
@@ -1337,8 +1358,9 @@ struct AstNodeCond : public AstNodeTriop {
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
};
|
||||
|
||||
struct AstNodePreSel : public AstNode {
|
||||
class AstNodePreSel : public AstNode {
|
||||
// Something that becomes an AstSel
|
||||
public:
|
||||
AstNodePreSel(FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths)
|
||||
: AstNode(fl) {
|
||||
setOp1p(lhs); setOp2p(rhs); setNOp3p(ths); }
|
||||
@@ -1357,8 +1379,9 @@ struct AstNodePreSel : public AstNode {
|
||||
virtual bool same(AstNode*) const { return true; }
|
||||
};
|
||||
|
||||
struct AstNodeStmt : public AstNode {
|
||||
class AstNodeStmt : public AstNode {
|
||||
// Statement -- anything that's directly under a function
|
||||
public:
|
||||
AstNodeStmt(FileLine* fl)
|
||||
: AstNode(fl) {}
|
||||
ASTNODE_BASE_FUNCS(NodeStmt)
|
||||
@@ -1367,7 +1390,8 @@ struct AstNodeStmt : public AstNode {
|
||||
virtual void addBeforeStmt(AstNode* newp, AstNode* belowp); // Stop statement searchback here
|
||||
};
|
||||
|
||||
struct AstNodeAssign : public AstNodeStmt {
|
||||
class AstNodeAssign : public AstNodeStmt {
|
||||
public:
|
||||
AstNodeAssign(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
||||
: AstNodeStmt(fl) {
|
||||
setOp1p(rhsp); setOp2p(lhsp);
|
||||
@@ -1388,7 +1412,8 @@ struct AstNodeAssign : public AstNodeStmt {
|
||||
virtual string verilogKwd() const { return "="; }
|
||||
};
|
||||
|
||||
struct AstNodeFor : public AstNodeStmt {
|
||||
class AstNodeFor : public AstNodeStmt {
|
||||
public:
|
||||
AstNodeFor(FileLine* fileline, AstNode* initsp, AstNode* condp,
|
||||
AstNode* incsp, AstNode* bodysp)
|
||||
: AstNodeStmt(fileline) {
|
||||
@@ -1405,7 +1430,7 @@ struct AstNodeFor : public AstNodeStmt {
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
};
|
||||
|
||||
struct AstNodeIf : public AstNodeStmt {
|
||||
class AstNodeIf : public AstNodeStmt {
|
||||
private:
|
||||
AstBranchPred m_branchPred; // Branch prediction as taken/untaken?
|
||||
public:
|
||||
@@ -1429,7 +1454,8 @@ public:
|
||||
AstBranchPred branchPred() const { return m_branchPred; }
|
||||
};
|
||||
|
||||
struct AstNodeCase : public AstNodeStmt {
|
||||
class AstNodeCase : public AstNodeStmt {
|
||||
public:
|
||||
AstNodeCase(FileLine* fl, AstNode* exprp, AstNode* casesp)
|
||||
: AstNodeStmt(fl) {
|
||||
setOp1p(exprp); addNOp2p(casesp);
|
||||
@@ -1443,8 +1469,9 @@ struct AstNodeCase : public AstNodeStmt {
|
||||
void addNotParallelp(AstNode* nodep) { setOp3p(nodep); }
|
||||
};
|
||||
|
||||
struct AstNodeSenItem : public AstNode {
|
||||
class AstNodeSenItem : public AstNode {
|
||||
// An AstSenItem or AstSenGate
|
||||
public:
|
||||
AstNodeSenItem(FileLine* fl) : AstNode(fl) {}
|
||||
ASTNODE_BASE_FUNCS(NodeSenItem)
|
||||
virtual bool isClocked() const = 0;
|
||||
@@ -1502,7 +1529,7 @@ public:
|
||||
void iterateChildren(AstNVisitor& v, AstNUser* vup=NULL) { }
|
||||
};
|
||||
|
||||
struct AstNodeText : public AstNode {
|
||||
class AstNodeText : public AstNode {
|
||||
private:
|
||||
string m_text;
|
||||
public:
|
||||
@@ -1519,11 +1546,11 @@ public:
|
||||
const string& text() const { return m_text; }
|
||||
};
|
||||
|
||||
struct AstNodeDType : public AstNode {
|
||||
private:
|
||||
class AstNodeDType : public AstNode {
|
||||
// Ideally width() would migrate to BasicDType as that's where it makes sense,
|
||||
// but it's currently so prevalent in the code we leave it here.
|
||||
// Note the below members are included in AstTypeTable::Key lookups
|
||||
private:
|
||||
int m_width; // (also in AstTypeTable::Key) Bit width of operation
|
||||
int m_widthMin; // (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation
|
||||
AstNumeric m_numeric; // (also in AstTypeTable::Key) Node is signed
|
||||
@@ -1541,8 +1568,9 @@ public:
|
||||
virtual void dumpSmall(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 to next non-typeref type
|
||||
virtual AstNodeDType* skipRefp() const = 0; // recurses over typedefs/const/enum to next non-typeref type
|
||||
virtual AstNodeDType* skipRefToConstp() const = 0; // recurses over typedefs to next non-typeref-or-const type
|
||||
virtual AstNodeDType* skipRefToEnump() const = 0; // recurses over typedefs/const to next non-typeref-or-enum/struct type
|
||||
virtual int widthAlignBytes() const = 0; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this)
|
||||
virtual int widthTotalBytes() const = 0; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,...
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
@@ -1572,7 +1600,7 @@ public:
|
||||
static int uniqueNumInc() { return ++s_uniqueNum; }
|
||||
};
|
||||
|
||||
struct AstNodeClassDType : public AstNodeDType {
|
||||
class AstNodeClassDType : public AstNodeDType {
|
||||
private:
|
||||
// TYPES
|
||||
typedef map<string,AstMemberDType*> MemberNameMap;
|
||||
@@ -1593,6 +1621,7 @@ public:
|
||||
virtual AstBasicDType* basicp() const { return findLogicDType(width(),width(),numeric())->castBasicDType(); }
|
||||
virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; }
|
||||
virtual int widthAlignBytes() const; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this)
|
||||
virtual int widthTotalBytes() const; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,...
|
||||
// op1 = members
|
||||
@@ -1611,7 +1640,7 @@ public:
|
||||
VNumRange declRange() const { return VNumRange(msb(), lsb(), false); }
|
||||
};
|
||||
|
||||
struct AstNodeArrayDType : public AstNodeDType {
|
||||
class AstNodeArrayDType : public AstNodeDType {
|
||||
// Array data type, ie "some_dtype var_name [2:0]"
|
||||
// Children: DTYPE (moved to refDTypep() in V3Width)
|
||||
// Children: RANGE (array bounds)
|
||||
@@ -1649,6 +1678,7 @@ public:
|
||||
virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type
|
||||
virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; }
|
||||
virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; }
|
||||
virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); }
|
||||
virtual int widthTotalBytes() const { return elementsConst() * subDTypep()->widthTotalBytes(); }
|
||||
int msb() const;
|
||||
@@ -1657,8 +1687,9 @@ public:
|
||||
VNumRange declRange() const;
|
||||
};
|
||||
|
||||
struct AstNodeSel : public AstNodeBiop {
|
||||
class AstNodeSel : public AstNodeBiop {
|
||||
// Single bit range extraction, perhaps with non-constant selection or array selection
|
||||
public:
|
||||
AstNodeSel(FileLine* fl, AstNode* fromp, AstNode* bitp)
|
||||
:AstNodeBiop(fl, fromp, bitp) {}
|
||||
ASTNODE_BASE_FUNCS(NodeSel)
|
||||
@@ -1670,10 +1701,21 @@ struct AstNodeSel : public AstNodeBiop {
|
||||
virtual bool hasDType() const { return true; }
|
||||
};
|
||||
|
||||
class AstNodeStream : public AstNodeBiop {
|
||||
// Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
|
||||
public:
|
||||
AstNodeStream(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
if (lhsp->dtypep()) {
|
||||
dtypeSetLogicSized(lhsp->dtypep()->width(), lhsp->dtypep()->width(), AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeStream)
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Tasks/functions common handling
|
||||
|
||||
struct AstNodeFTask : public AstNode {
|
||||
class AstNodeFTask : public AstNode {
|
||||
private:
|
||||
string m_name; // Name of task
|
||||
string m_cname; // Name of task if DPI import
|
||||
@@ -1731,7 +1773,7 @@ public:
|
||||
bool pure() const { return m_pure; }
|
||||
};
|
||||
|
||||
struct AstNodeFTaskRef : public AstNode {
|
||||
class AstNodeFTaskRef : public AstNode {
|
||||
// A reference to a task (or function)
|
||||
private:
|
||||
AstNodeFTask* m_taskp; // [AfterLink] Pointer to task referenced
|
||||
@@ -1777,7 +1819,7 @@ public:
|
||||
void scopeNamep(AstNode* nodep) { setNOp3p(nodep); }
|
||||
};
|
||||
|
||||
struct AstNodeModule : public AstNode {
|
||||
class AstNodeModule : public AstNode {
|
||||
// A module, package, program or interface declaration;
|
||||
// something that can live directly under the TOP,
|
||||
// excluding $unit package stuff
|
||||
@@ -1791,12 +1833,13 @@ private:
|
||||
bool m_internal:1; // Internally created
|
||||
int m_level; // 1=top module, 2=cell off top module, ...
|
||||
int m_varNum; // Incrementing variable number
|
||||
int m_typeNum; // Incrementing implicit type number
|
||||
public:
|
||||
AstNodeModule(FileLine* fl, const string& name)
|
||||
: AstNode (fl)
|
||||
,m_name(name), m_origName(name)
|
||||
,m_modPublic(false), m_modTrace(false), m_inLibrary(false), m_dead(false), m_internal(false)
|
||||
,m_level(0), m_varNum(0) { }
|
||||
,m_level(0), m_varNum(0), m_typeNum(0) { }
|
||||
ASTNODE_BASE_FUNCS(NodeModule)
|
||||
virtual void dump(ostream& str);
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
@@ -1816,6 +1859,7 @@ public:
|
||||
int level() const { return m_level; }
|
||||
bool isTop() const { return level()==1; }
|
||||
int varNumGetInc() { return ++m_varNum; }
|
||||
int typeNumGetInc() { return ++m_typeNum; }
|
||||
void modPublic(bool flag) { m_modPublic = flag; }
|
||||
bool modPublic() const { return m_modPublic; }
|
||||
void modTrace(bool flag) { m_modTrace = flag; }
|
||||
@@ -1849,6 +1893,8 @@ inline bool AstNode::isNeqZero() { return (this->castConst() && this->castConst
|
||||
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(node2p, true, false); }
|
||||
inline bool AstNode::sameGateTree(AstNode* node2p) { return sameTreeIter(node2p, true, true); }
|
||||
|
||||
inline void AstNodeVarRef::init() { if (m_varp) dtypep(m_varp->dtypep()); }
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Ast node structure
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2014 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 _V3ASTCONSTONLY_H_
|
||||
#define _V3ASTCONSTONLY_H_ 1
|
||||
|
||||
// Include only in visitors that do not not edit nodes, so should use constant iterators
|
||||
#define iterateAndNext error_use_iterateAndNextConst
|
||||
#define iterateChildren error_use_iterateChildrenConst
|
||||
|
||||
#define addNext error_no_addNext_in_ConstOnlyVisitor
|
||||
#define replaceWith error_no_replaceWith_in_ConstOnlyVisitor
|
||||
#define deleteTree error_no_deleteTree_in_ConstOnlyVisitor
|
||||
#define unlinkFrBack error_no_unlinkFrBack_in_ConstOnlyVisitor
|
||||
|
||||
#endif // Guard
|
||||
+56
-5
@@ -112,6 +112,35 @@ int AstNodeClassDType::widthAlignBytes() const {
|
||||
else return 8;
|
||||
}
|
||||
|
||||
AstNodeBiop* AstEq::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstEqD(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstEq(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeBiop* AstGte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstGteD(fl, lhsp, rhsp);
|
||||
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
||||
return new AstGteS(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstGte(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeBiop* AstLte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstLteD(fl, lhsp, rhsp);
|
||||
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
||||
return new AstLteS(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstLte(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool AstVar::isSigPublic() const {
|
||||
return (m_sigPublic || (v3Global.opt.allPublic() && !isTemp() && !isGenVar()));
|
||||
}
|
||||
@@ -171,7 +200,7 @@ string AstVar::verilogKwd() const {
|
||||
}
|
||||
}
|
||||
|
||||
string AstVar::vlArgType(bool named, bool forReturn) const {
|
||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const {
|
||||
if (forReturn) named=false;
|
||||
if (forReturn) v3fatalSrc("verilator internal data is never passed as return, but as first argument");
|
||||
string arg;
|
||||
@@ -204,7 +233,7 @@ string AstVar::vlArgType(bool named, bool forReturn) const {
|
||||
arg += " (& "+name();
|
||||
arg += ")["+cvtToStr(widthWords())+"]";
|
||||
} else {
|
||||
if (isOutput() || (strtype && isInput())) arg += "&";
|
||||
if (forFunc && (isOutput() || (strtype && isInput()))) arg += "&";
|
||||
if (named) arg += " "+name();
|
||||
}
|
||||
return arg;
|
||||
@@ -486,6 +515,24 @@ string AstScopeName::scopeSymName() const {
|
||||
return out;
|
||||
}
|
||||
|
||||
string AstScopeName::scopeDpiName() const {
|
||||
string out;
|
||||
for (AstText* textp=scopeEntrp(); textp; textp=textp->nextp()->castText()) {
|
||||
out += textp->text();
|
||||
}
|
||||
if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,"");
|
||||
if (out.substr(0,7) == "__DOT__") out.replace(0,7,"");
|
||||
if (out.substr(0,1) == ".") out.replace(0,1,"");
|
||||
string::size_type pos;
|
||||
while ((pos=out.find(".")) != string::npos) {
|
||||
out.replace(pos, 1, "__");
|
||||
}
|
||||
while ((pos=out.find("__DOT__")) != string::npos) {
|
||||
out.replace(pos, 7, "__");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool AstSenTree::hasClocked() {
|
||||
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
|
||||
for (AstNodeSenItem* senp = sensesp(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
@@ -772,6 +819,10 @@ void AstPin::dump(ostream& str) {
|
||||
else { str<<" ->UNLINKED"; }
|
||||
if (svImplicit()) str<<" [.SV]";
|
||||
}
|
||||
void AstTypedef::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (attrPublic()) str<<" [PUBLIC]";
|
||||
}
|
||||
void AstRange::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (littleEndian()) str<<" [LITTLE]";
|
||||
@@ -806,11 +857,11 @@ void AstNodeDType::dumpSmall(ostream& str) {
|
||||
void AstNodeArrayDType::dumpSmall(ostream& str) {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
if (castPackArrayDType()) str<<"p"; else str<<"u";
|
||||
str<<"["<<declRange().left()<<":"<<declRange().right()<<"]";
|
||||
str<<" "<<declRange();
|
||||
}
|
||||
void AstNodeArrayDType::dump(ostream& str) {
|
||||
this->AstNodeDType::dump(str);
|
||||
str<<" ["<<declRange().left()<<":"<<declRange().right()<<"]";
|
||||
str<<" "<<declRange();
|
||||
}
|
||||
void AstNodeModule::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
@@ -826,7 +877,7 @@ void AstPackageImport::dump(ostream& str) {
|
||||
void AstSel::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (declRange().ranged()) {
|
||||
str<<" decl["<<declRange().left()<<":"<<declRange().right()<<"]";
|
||||
str<<" decl"<<declRange()<<"]";
|
||||
if (declElWidth()!=1) str<<"/"<<declElWidth();
|
||||
}
|
||||
}
|
||||
|
||||
+549
-266
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -94,9 +94,9 @@ private:
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
BranchVisitor(AstNetlist* rootp) {
|
||||
BranchVisitor(AstNetlist* nodep) {
|
||||
reset();
|
||||
rootp->iterateChildren(*this);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual ~BranchVisitor() {}
|
||||
};
|
||||
|
||||
+12
-8
@@ -37,6 +37,9 @@
|
||||
#include "V3Broken.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
|
||||
#include "V3AstConstOnly.h"
|
||||
|
||||
//######################################################################
|
||||
|
||||
class BrokenTable : public AstNVisitor {
|
||||
@@ -47,11 +50,11 @@ private:
|
||||
typedef map<const AstNode*,int> NodeMap;
|
||||
static NodeMap s_nodes; // Set of all nodes that exist
|
||||
// BITMASK
|
||||
static const int FLAG_ALLOCATED = 0x01; // new() and not delete()ed
|
||||
static const int FLAG_IN_TREE = 0x02; // Is in netlist tree
|
||||
static const int FLAG_LINKABLE = 0x04; // Is in netlist tree, can be linked to
|
||||
static const int FLAG_LEAKED = 0x08; // Known to have been leaked
|
||||
static const int FLAG_UNDER_NOW = 0x10; // Is in tree as parent of current node
|
||||
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
|
||||
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
|
||||
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
|
||||
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
|
||||
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
|
||||
public:
|
||||
// METHODS
|
||||
static void deleted(const AstNode* nodep) {
|
||||
@@ -71,7 +74,8 @@ public:
|
||||
((AstNode*)(nodep))->v3fatalSrc("Newing AstNode object that is already allocated\n");
|
||||
}
|
||||
if (iter == s_nodes.end()) {
|
||||
s_nodes.insert(make_pair(nodep,FLAG_ALLOCATED));
|
||||
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
|
||||
s_nodes.insert(make_pair(nodep,flags));
|
||||
}
|
||||
}
|
||||
static void setUnder(const AstNode* nodep, bool flag) {
|
||||
@@ -185,7 +189,7 @@ private:
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
@@ -228,7 +232,7 @@ private:
|
||||
nodep->v3fatalSrc("Width != WidthMin");
|
||||
}
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
BrokenTable::setUnder(nodep,false);
|
||||
}
|
||||
public:
|
||||
|
||||
+24
-17
@@ -97,7 +97,8 @@ private:
|
||||
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()) {
|
||||
// Don't sweat it, we already complained about casex in general
|
||||
} else if (m_caseExprp->castCase() && m_caseExprp->castCase()->casez()) {
|
||||
} else if (m_caseExprp->castCase() && (m_caseExprp->castCase()->casez()
|
||||
|| m_caseExprp->castCase()->caseInside())) {
|
||||
if (nodep->num().isUnknown()) {
|
||||
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)");
|
||||
}
|
||||
@@ -322,36 +323,42 @@ private:
|
||||
icondNextp = icondp->nextp();
|
||||
icondp->unlinkFrBack();
|
||||
|
||||
AstNode* and1p;
|
||||
AstNode* and2p;
|
||||
AstNode* condp = NULL; // Default is to use and1p/and2p
|
||||
AstConst* iconstp = icondp->castConst();
|
||||
if (iconstp && neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
icondp->deleteTree(); icondp=NULL; iconstp=NULL;
|
||||
// For simplicity, make expression that is not equal, and let later
|
||||
// optimizations remove it
|
||||
and1p = new AstConst(itemp->fileline(), AstConst::LogicFalse());
|
||||
and2p = new AstConst(itemp->fileline(), AstConst::LogicTrue());
|
||||
condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
|
||||
} else if (AstInsideRange* irangep = icondp->castInsideRange()) {
|
||||
// Similar logic in V3Width::visit(AstInside)
|
||||
AstNode* ap = AstGte::newTyped(itemp->fileline(),
|
||||
cexprp->cloneTree(false),
|
||||
irangep->lhsp()->unlinkFrBack());
|
||||
AstNode* bp = AstLte::newTyped(itemp->fileline(),
|
||||
cexprp->cloneTree(false),
|
||||
irangep->rhsp()->unlinkFrBack());
|
||||
condp = new AstAnd(itemp->fileline(), ap, bp);
|
||||
} else if (iconstp && iconstp->num().isFourState()
|
||||
&& (nodep->casex() || nodep->casez())) {
|
||||
&& (nodep->casex() || nodep->casez() || nodep->caseInside())) {
|
||||
V3Number nummask (itemp->fileline(), iconstp->width());
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
V3Number numval (itemp->fileline(), iconstp->width());
|
||||
numval.opBitsOne(iconstp->num());
|
||||
and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
and2p = new AstAnd(itemp->fileline(),
|
||||
new AstConst(itemp->fileline(), numval),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
AstNode* and2p = new AstAnd(itemp->fileline(),
|
||||
new AstConst(itemp->fileline(), numval),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
icondp->deleteTree(); icondp=NULL; iconstp=NULL;
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
} else {
|
||||
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
|
||||
and1p = cexprp->cloneTree(false);
|
||||
and2p = icondp;
|
||||
AstNode* and1p = cexprp->cloneTree(false);
|
||||
AstNode* and2p = icondp;
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
}
|
||||
AstNodeBiop* condp = (and1p->isDouble()
|
||||
? (new AstEqD(itemp->fileline(), and1p, and2p))->castNodeBiop()
|
||||
: (new AstEq(itemp->fileline(), and1p, and2p))->castNodeBiop());
|
||||
if (!ifexprp) {
|
||||
ifexprp = condp;
|
||||
} else {
|
||||
@@ -435,7 +442,7 @@ private:
|
||||
bool neverItem(AstCase* casep, AstConst* itemp) {
|
||||
// Xs in case or casez are impossible due to two state simulations
|
||||
if (casep->casex()) {
|
||||
} else if (casep->casez()) {
|
||||
} else if (casep->casez() || casep->caseInside()) {
|
||||
if (itemp->num().isUnknown()) return true;
|
||||
} else {
|
||||
if (itemp->num().isFourState()) return true;
|
||||
|
||||
@@ -712,6 +712,7 @@ private:
|
||||
virtual void visit(AstInitial* nodep, AstNUser*) { }
|
||||
virtual void visit(AstTraceInc* nodep, AstNUser*) { }
|
||||
virtual void visit(AstCoverToggle* nodep, AstNUser*) { }
|
||||
virtual void visit(AstNodeDType* nodep, AstNUser*) { }
|
||||
|
||||
//--------------------
|
||||
// Default
|
||||
|
||||
+14
-15
@@ -81,22 +81,21 @@ private:
|
||||
}
|
||||
|
||||
void genChangeDet(AstVarScope* vscp) {
|
||||
#ifdef NEW_ORDERING
|
||||
vscp->v3fatalSrc("Not applicable\n");
|
||||
#endif
|
||||
AstVar* varp = vscp->varp();
|
||||
vscp->v3warn(IMPERFECTSCH,"Imperfect scheduling of variable: "<<vscp);
|
||||
AstUnpackArrayDType* arrayp = varp->dtypeSkipRefp()->castUnpackArrayDType();
|
||||
AstStructDType *structp = varp->dtypeSkipRefp()->castStructDType();
|
||||
bool isArray = arrayp;
|
||||
bool isStruct = structp && structp->packedUnsup();
|
||||
int elements = isArray ? arrayp->elementsConst() : 1;
|
||||
if (isArray && (elements > DETECTARRAY_MAX_INDEXES)) {
|
||||
AstUnpackArrayDType* uarrayp = varp->dtypeSkipRefp()->castUnpackArrayDType();
|
||||
AstPackArrayDType* parrayp = varp->dtypeSkipRefp()->castPackArrayDType();
|
||||
AstNodeClassDType *classp = varp->dtypeSkipRefp()->castNodeClassDType();
|
||||
bool isUnpackArray = uarrayp;
|
||||
bool isPackArray = parrayp;
|
||||
bool isClass = classp && classp->packedUnsup();
|
||||
int elements = isUnpackArray ? uarrayp->elementsConst() : 1;
|
||||
if (isUnpackArray && (elements > DETECTARRAY_MAX_INDEXES)) {
|
||||
vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than "<<cvtToStr(DETECTARRAY_MAX_INDEXES)
|
||||
<<" array indexes (probably with UNOPTFLAT warning suppressed): "<<varp->prettyName()<<endl
|
||||
<<vscp->warnMore()
|
||||
<<"... Could recompile with DETECTARRAY_MAX_INDEXES increased to at least "<<cvtToStr(elements));
|
||||
} else if (!isArray && !isStruct
|
||||
} else if (!isUnpackArray && !isClass && !isPackArray
|
||||
&& !varp->dtypeSkipRefp()->castBasicDType()) {
|
||||
if (debug()) varp->dumpTree(cout,"-DETECTARRAY-");
|
||||
vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable (probably with UNOPTFLAT warning suppressed): "<<varp->prettyName());
|
||||
@@ -113,17 +112,17 @@ private:
|
||||
for (int index=0; index<elements; ++index) {
|
||||
AstChangeDet* changep
|
||||
= new AstChangeDet (vscp->fileline(),
|
||||
aselIfNeeded(isArray, index,
|
||||
aselIfNeeded(isUnpackArray, index,
|
||||
new AstVarRef(vscp->fileline(), vscp, false)),
|
||||
aselIfNeeded(isArray, index,
|
||||
aselIfNeeded(isUnpackArray, index,
|
||||
new AstVarRef(vscp->fileline(), newvscp, false)),
|
||||
false);
|
||||
m_chgFuncp->addStmtsp(changep);
|
||||
AstAssign* initp
|
||||
= new AstAssign (vscp->fileline(),
|
||||
aselIfNeeded(isArray, index,
|
||||
aselIfNeeded(isUnpackArray, index,
|
||||
new AstVarRef(vscp->fileline(), newvscp, true)),
|
||||
aselIfNeeded(isArray, index,
|
||||
aselIfNeeded(isUnpackArray, index,
|
||||
new AstVarRef(vscp->fileline(), vscp, false)));
|
||||
m_chgFuncp->addFinalsp(initp);
|
||||
}
|
||||
@@ -147,7 +146,7 @@ private:
|
||||
if (!scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?\n");
|
||||
m_scopetopp = scopep;
|
||||
// Create change detection function
|
||||
m_chgFuncp = new AstCFunc(nodep->fileline(), "_change_request", scopep, "IData");
|
||||
m_chgFuncp = new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData");
|
||||
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_chgFuncp->symProlog(true);
|
||||
m_chgFuncp->declPrivate(true);
|
||||
|
||||
@@ -238,6 +238,10 @@ private:
|
||||
nodep->iterateChildren(*this);
|
||||
insureCleanAndNext (nodep->valuep());
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep, AstNUser*) {
|
||||
// No cleaning, or would loose pointer to enum
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
// Control flow operators
|
||||
virtual void visit(AstNodeCond* nodep, AstNUser*) {
|
||||
|
||||
+10
-135
@@ -320,28 +320,6 @@ private:
|
||||
}
|
||||
nodep->deleteTree(); nodep = NULL;
|
||||
}
|
||||
void moveInitial(AstActive* nodep) {
|
||||
// Change to CFunc
|
||||
AstNode* stmtsp = nodep->stmtsp();
|
||||
if (stmtsp) {
|
||||
if (!m_scopep) nodep->v3fatalSrc("Initial Active not under scope\n");
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_initial__"+m_scopep->nameDotless(),
|
||||
m_scopep);
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->symProlog(true);
|
||||
funcp->slow(true);
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
funcp->addStmtsp(stmtsp);
|
||||
nodep->replaceWith(funcp);
|
||||
// Add top level call to it
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), funcp);
|
||||
callp->argTypes("vlSymsp");
|
||||
m_initFuncp->addStmtsp(callp);
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
// Link to global function
|
||||
@@ -365,13 +343,14 @@ private:
|
||||
if (m_untilp) m_untilp->addBodysp(stmtsp); // In a until loop, add to body
|
||||
else m_settleFuncp->addStmtsp(stmtsp); // else add to top level function
|
||||
}
|
||||
void addToInitial(AstNode* stmtsp) {
|
||||
if (m_untilp) m_untilp->addBodysp(stmtsp); // In a until loop, add to body
|
||||
else m_initFuncp->addStmtsp(stmtsp); // else add to top level function
|
||||
}
|
||||
virtual void visit(AstActive* nodep, AstNUser*) {
|
||||
// Careful if adding variables here, ACTIVES can be under other ACTIVES
|
||||
// Need to save and restore any member state in AstUntilStable block
|
||||
if (nodep->hasInitial()) {
|
||||
moveInitial(nodep);
|
||||
}
|
||||
else if (!m_topScopep || !nodep->stmtsp()) {
|
||||
if (!m_topScopep || !nodep->stmtsp()) {
|
||||
// Not at the top or empty block...
|
||||
// Only empty blocks should be leftover on the non-top. Killem.
|
||||
if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active");
|
||||
@@ -381,6 +360,7 @@ private:
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (nodep->hasClocked()) {
|
||||
// Remember the latest sensitivity so we can compare it next time
|
||||
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 {
|
||||
@@ -392,6 +372,10 @@ private:
|
||||
}
|
||||
// Move statements to if
|
||||
m_lastIfp->addIfsp(stmtsp);
|
||||
} else if (nodep->hasInitial()) {
|
||||
// Don't need to: clearLastSen();, as we're adding it to different cfunc
|
||||
// Move statements to function
|
||||
addToInitial(stmtsp);
|
||||
} else if (nodep->hasSettle()) {
|
||||
// Don't need to: clearLastSen();, as we're adding it to different cfunc
|
||||
// Move statements to function
|
||||
@@ -406,115 +390,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NEW_ORDERING
|
||||
virtual void visit(AstUntilStable* nodep, AstNUser*) {
|
||||
// Process any sub ACTIVE statements first
|
||||
UINFO(4," UNTILSTABLE "<<nodep<<endl);
|
||||
{
|
||||
// Keep vars if in middle of other stable
|
||||
AstUntilStable* lastUntilp = m_untilp;
|
||||
AstSenTree* lastSenp = m_lastSenp;
|
||||
AstIf* lastIfp = m_lastIfp;
|
||||
m_untilp = nodep;
|
||||
m_lastSenp = NULL;
|
||||
m_lastIfp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
m_untilp = lastUntilp;
|
||||
m_lastSenp = lastSenp;
|
||||
m_lastIfp = lastIfp;
|
||||
}
|
||||
// Set "unstable" to 100. (non-stabilization count)
|
||||
// int __VclockLoop = 0
|
||||
// IData __Vchange = 1
|
||||
// while (__Vchange) {
|
||||
// Save old values of each until stable variable
|
||||
// Evaluate the body
|
||||
// __Vchange = {change_detect} contribution
|
||||
// if (++__VclockLoop > 100) converge_error
|
||||
if (debug()>4) nodep->dumpTree(cout, " UntilSt-old: ");
|
||||
FileLine* fl = nodep->fileline();
|
||||
if (nodep->bodysp()) fl = nodep->bodysp()->fileline(); // Point to applicable code...
|
||||
m_stableNum++;
|
||||
AstNode* origBodysp = nodep->bodysp(); if (origBodysp) origBodysp->unlinkFrBackWithNext();
|
||||
AstVarScope* changeVarp = getCreateLocalVar(fl, "__Vchange"+cvtToStr(m_stableNum), NULL, 32);
|
||||
AstVarScope* countVarp = getCreateLocalVar(fl, "__VloopCount"+cvtToStr(m_stableNum), NULL, 32);
|
||||
AstWhile* untilp = new AstWhile(fl, new AstVarRef(fl, changeVarp, false), NULL);
|
||||
AstNode* preUntilp = new AstComment(fl, "Change loop "+cvtToStr(m_stableNum));
|
||||
preUntilp->addNext(new AstAssign(fl, new AstVarRef(fl, changeVarp, true),
|
||||
new AstConst(fl, 1)));
|
||||
preUntilp->addNext(new AstAssign(fl, new AstVarRef(fl, countVarp, true),
|
||||
new AstConst(fl, 0)));
|
||||
|
||||
// Add stable variables & preinits
|
||||
AstNode* setChglastp = NULL;
|
||||
for (AstVarRef* varrefp = nodep->stablesp(); varrefp; varrefp=varrefp->nextp()->castVarRef()) {
|
||||
AstVarScope* cmpvscp = getCreateLocalVar(varrefp->varp()->fileline(),
|
||||
"__Vchglast"+cvtToStr(m_stableNum)+"__"+varrefp->name(),
|
||||
varrefp->varp(), 0);
|
||||
varrefp->varScopep()->user2p(cmpvscp);
|
||||
setChglastp = setChglastp->addNext(
|
||||
new AstAssign(fl, new AstVarRef(fl, cmpvscp, true),
|
||||
new AstVarRef(fl, varrefp->varScopep(), false)));
|
||||
}
|
||||
if (!setChglastp) nodep->v3fatalSrc("UntilStable without any variables");
|
||||
untilp->addBodysp(setChglastp);
|
||||
untilp->addBodysp(new AstComment(fl, "Change Loop body begin"));
|
||||
if (origBodysp) untilp->addBodysp(origBodysp);
|
||||
untilp->addBodysp(new AstComment(fl, "Change Loop body end"));
|
||||
// Add stable checks
|
||||
// The order of the variables doesn't matter, but it's more expensive to test
|
||||
// wide variables, and more so 64 bit wide ones. Someday it might be faster to
|
||||
// set the changed variable in a "distributed" fashion over the code, IE,
|
||||
// logic... logic.... a=....; Changed |= (a ^ old_a); more logic... Changed |=...
|
||||
// But then, one hopes users don't have much unoptimized logic
|
||||
AstNode* changeExprp = NULL;
|
||||
int doublecount = 0;
|
||||
for (int wide=0; wide<3; wide++) { // Adding backwards; wide, quad, then normal
|
||||
for (AstVarRef* varrefp = nodep->stablesp(); varrefp; varrefp=varrefp->nextp()->castVarRef()) {
|
||||
if (wide== ( varrefp->isQuad()?0:(varrefp->isWide() ? 1:2))) {
|
||||
AstVarScope* cmpvscp = (AstVarScope*)(varrefp->varScopep()->user2p());
|
||||
if (!cmpvscp) varrefp->v3fatalSrc("should have created above");
|
||||
AstChangeXor* changep
|
||||
= new AstChangeXor (fl,
|
||||
new AstVarRef(fl, varrefp->varScopep(), false),
|
||||
new AstVarRef(fl, cmpvscp, false));
|
||||
if (!changeExprp) changeExprp = changep;
|
||||
else if (doublecount++ > DOUBLE_OR_RATE) {
|
||||
doublecount = 0;
|
||||
changeExprp = new AstLogOr (fl, changep, changeExprp);
|
||||
} else {
|
||||
changeExprp = new AstOr (fl, changep, changeExprp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!changeExprp) nodep->v3fatalSrc("UntilStable without any variables");
|
||||
changeExprp = new AstAssign(fl, new AstVarRef(fl, changeVarp, true),
|
||||
changeExprp);
|
||||
untilp->addBodysp(changeExprp);
|
||||
//
|
||||
// Final body
|
||||
AstNode* ifstmtp = new AstAssign(fl, new AstVarRef(fl, countVarp, true),
|
||||
new AstAdd(fl, new AstConst(fl, 1),
|
||||
new AstVarRef(fl, countVarp, false)));
|
||||
ifstmtp->addNext(new AstIf(fl,
|
||||
new AstLt (fl, new AstConst(fl, 100),
|
||||
new AstVarRef(fl, countVarp, false)),
|
||||
(new AstDisplay (fl, AstDisplayType::DT_DISPLAY,
|
||||
"%%Error: Verilated model didn't converge", NULL, NULL))
|
||||
->addNext(new AstStop (fl)),
|
||||
NULL));
|
||||
untilp->addBodysp(new AstIf(fl, new AstNeq(fl, new AstConst(fl, 0),
|
||||
new AstVarRef(fl, changeVarp, false)),
|
||||
ifstmtp, NULL));
|
||||
//
|
||||
// Replace it
|
||||
preUntilp->addNext(untilp);
|
||||
if (debug()>4) preUntilp->dumpTreeAndNext(cout, " UntilSt-new: ");
|
||||
nodep->replaceWith(preUntilp); nodep->deleteTree(); nodep=NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
|
||||
+307
-42
@@ -106,7 +106,8 @@ private:
|
||||
bool m_doShort; // Remove expressions that short circuit
|
||||
bool m_doV; // Verilog, not C++ conversion
|
||||
bool m_doGenerate; // Postpone width checking inside generate
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstArraySel* m_selp; // Current select
|
||||
AstNode* m_scopep; // Current scope
|
||||
AstAttrOf* m_attrp; // Current attribute
|
||||
|
||||
@@ -172,6 +173,16 @@ private:
|
||||
if (rnodep->width() != bnodep->width()) return false;
|
||||
return rnodep->lhsp()->castConst();
|
||||
}
|
||||
static bool operandSubAdd(AstNode* nodep) {
|
||||
// SUB( ADD(CONSTx,y), CONSTz) -> ADD(SUB(CONSTx,CONSTz), y)
|
||||
AstNodeBiop* np = nodep->castNodeBiop();
|
||||
AstNodeBiop* lp = np->lhsp()->castNodeBiop();
|
||||
return (lp
|
||||
&& lp->lhsp()->castConst()
|
||||
&& np->rhsp()->castConst()
|
||||
&& lp->width()==np->width());
|
||||
}
|
||||
|
||||
static bool operandAndOrSame(AstNode* nodep) {
|
||||
// OR( AND(VAL,x), AND(VAL,y)) -> AND(VAL,OR(x,y))
|
||||
// OR( AND(x,VAL), AND(y,VAL)) -> AND(OR(x,y),VAL)
|
||||
@@ -233,16 +244,19 @@ private:
|
||||
}
|
||||
bool operandHugeShiftL(AstNodeBiop* nodep) {
|
||||
return (nodep->rhsp()->castConst()
|
||||
&& !nodep->rhsp()->castConst()->num().isFourState()
|
||||
&& nodep->rhsp()->castConst()->toUInt() >= (uint32_t)(nodep->width())
|
||||
&& isTPure(nodep->lhsp()));
|
||||
}
|
||||
bool operandHugeShiftR(AstNodeBiop* nodep) {
|
||||
return (nodep->rhsp()->castConst()
|
||||
&& !nodep->rhsp()->castConst()->num().isFourState()
|
||||
&& nodep->rhsp()->castConst()->toUInt() >= (uint32_t)(nodep->lhsp()->width())
|
||||
&& isTPure(nodep->lhsp()));
|
||||
}
|
||||
bool operandIsTwo(AstNode* nodep) {
|
||||
return (nodep->castConst()
|
||||
&& !nodep->castConst()->num().isFourState()
|
||||
&& nodep->width() <= VL_QUADSIZE
|
||||
&& nodep->castConst()->toUQuad()==2);
|
||||
}
|
||||
@@ -270,6 +284,8 @@ private:
|
||||
// We can only get rid of a<<b>>c or a<<b<<c, with constant b & c
|
||||
// because bits may be masked in that process, or (b+c) may exceed the word width.
|
||||
if (!(nodep->rhsp()->castConst() && lhsp->rhsp()->castConst())) return false;
|
||||
if (nodep->rhsp()->castConst()->num().isFourState()
|
||||
|| lhsp->rhsp()->castConst()->num().isFourState()) return false;
|
||||
if (nodep->width()!=lhsp->width()) return false;
|
||||
if (nodep->width()!=lhsp->lhsp()->width()) return false;
|
||||
return true;
|
||||
@@ -327,27 +343,27 @@ private:
|
||||
}
|
||||
|
||||
bool operandBiExtendConst(AstNodeBiop* nodep) {
|
||||
// Loop unrolling likes standalone compares
|
||||
// EQ(EXTEND(xx{width3}), const{width32}) -> EQ(xx{3},const{3})
|
||||
// Loop unrolling favors standalone compares
|
||||
// EQ(const{width32}, EXTEND(xx{width3})) -> EQ(const{3}, xx{3})
|
||||
// Beware that the constant must have zero bits (+ 1 if signed) or compare
|
||||
// would be incorrect
|
||||
AstExtend* extendp = nodep->lhsp()->castExtend();
|
||||
AstExtend* extendp = nodep->rhsp()->castExtend();
|
||||
if (!extendp) return false;
|
||||
AstNode* smallerp = extendp->lhsp();
|
||||
int subsize = smallerp->width();
|
||||
AstConst* constp = nodep->rhsp()->castConst();
|
||||
AstConst* constp = nodep->lhsp()->castConst();
|
||||
if (!constp) return false;
|
||||
if (!constp->num().isBitsZero(constp->width()-1, subsize)) return false;
|
||||
//
|
||||
if (debug()>=9) nodep->dumpTree(cout,"BI(EXTEND)-in:");
|
||||
smallerp->unlinkFrBack();
|
||||
extendp->unlinkFrBack()->deleteTree(); // aka nodep->lhsp.
|
||||
nodep->lhsp(smallerp);
|
||||
nodep->rhsp(smallerp);
|
||||
|
||||
constp->unlinkFrBack();
|
||||
V3Number num (constp->fileline(), subsize);
|
||||
num.opAssign(constp->num());
|
||||
nodep->rhsp(new AstConst(constp->fileline(), num));
|
||||
nodep->lhsp(new AstConst(constp->fileline(), num));
|
||||
constp->deleteTree(); constp=NULL;
|
||||
if (debug()>=9) nodep->dumpTree(cout,"BI(EXTEND)-ou:");
|
||||
return true;
|
||||
@@ -412,12 +428,12 @@ private:
|
||||
static bool operandsSame(AstNode* node1p, AstNode* node2p) {
|
||||
// For now we just detect constants & simple vars, though it could be more generic
|
||||
if (node1p->castConst() && node2p->castConst()) {
|
||||
return node1p->sameTree(node2p);
|
||||
return node1p->sameGateTree(node2p);
|
||||
}
|
||||
else if (node1p->castVarRef() && node2p->castVarRef()) {
|
||||
// Avoid comparing widthMin's, which results in lost optimization attempts
|
||||
// If cleanup sameTree to be smarter, this can be restored.
|
||||
//return node1p->sameTree(node2p);
|
||||
// If cleanup sameGateTree to be smarter, this can be restored.
|
||||
//return node1p->sameGateTree(node2p);
|
||||
return node1p->same(node2p);
|
||||
} else {
|
||||
return false;
|
||||
@@ -433,7 +449,7 @@ private:
|
||||
AstVarRef* elsevarp = elsep->lhsp()->castVarRef();
|
||||
if (!ifvarp || !elsevarp) return false;
|
||||
if (ifvarp->isWide()) return false; // Would need temporaries, so not worth it
|
||||
if (!ifvarp->sameTree(elsevarp)) return false;
|
||||
if (!ifvarp->sameGateTree(elsevarp)) return false;
|
||||
return true;
|
||||
}
|
||||
bool operandIfIf(AstNodeIf* nodep) {
|
||||
@@ -444,6 +460,89 @@ private:
|
||||
if (afterComment(lowerIfp->elsesp())) return false;
|
||||
return true;
|
||||
}
|
||||
bool ifConcatMergeableBiop(AstNode* nodep) {
|
||||
return (nodep->castAnd()
|
||||
|| nodep->castOr()
|
||||
|| nodep->castXor()
|
||||
|| nodep->castXnor());
|
||||
}
|
||||
bool ifAdjacentSel(AstSel* lhsp, AstSel* rhsp) {
|
||||
if (!v3Global.opt.oAssemble()) return false; // opt disabled
|
||||
if (!lhsp || !rhsp) return false;
|
||||
AstNode* lfromp = lhsp->fromp();
|
||||
AstNode* rfromp = rhsp->fromp();
|
||||
if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false;
|
||||
AstConst* lstart = lhsp->lsbp()->castConst();
|
||||
AstConst* rstart = rhsp->lsbp()->castConst();
|
||||
AstConst* lwidth = lhsp->widthp()->castConst();
|
||||
AstConst* rwidth = rhsp->widthp()->castConst();
|
||||
if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated
|
||||
int rend = (rstart->toSInt() + rwidth->toSInt());
|
||||
if (rend == lstart->toSInt()) return true;
|
||||
return false;
|
||||
}
|
||||
bool ifMergeAdjacent(AstNode* lhsp, AstNode* rhsp) {
|
||||
// called by concatmergeable to determine if {lhsp, rhsp} make sense
|
||||
if (!v3Global.opt.oAssemble()) return false; // opt disabled
|
||||
// two same varref
|
||||
if (operandsSame(lhsp, rhsp)) return true;
|
||||
AstSel* lselp = lhsp->castSel();
|
||||
AstSel* rselp = rhsp->castSel();
|
||||
// a[i:0] a
|
||||
if (lselp && !rselp && rhsp->sameGateTree(lselp->fromp()))
|
||||
rselp = new AstSel(rhsp->fileline(), rhsp->cloneTree(false), 0, rhsp->width());
|
||||
// a[i:j] {a[j-1:k], b}
|
||||
if (lselp && !rselp && rhsp->castConcat())
|
||||
return ifMergeAdjacent(lhsp, rhsp->castConcat()->lhsp());
|
||||
// a a[msb:j]
|
||||
if (rselp && !lselp && lhsp->sameGateTree(rselp->fromp()))
|
||||
lselp = new AstSel(lhsp->fileline(), lhsp->cloneTree(false), 0, lhsp->width());
|
||||
// {b, a[j:k]} a[k-1:i]
|
||||
if (rselp && !lselp && lhsp->castConcat())
|
||||
return ifMergeAdjacent(lhsp->castConcat()->rhsp(), rhsp);
|
||||
if (!lselp || !rselp) return false;
|
||||
|
||||
// a[a:b] a[b-1:c] are adjacent
|
||||
AstNode* lfromp = lselp->fromp();
|
||||
AstNode* rfromp = rselp->fromp();
|
||||
if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false;
|
||||
AstConst* lstart = lselp->lsbp()->castConst();
|
||||
AstConst* rstart = rselp->lsbp()->castConst();
|
||||
AstConst* lwidth = lselp->widthp()->castConst();
|
||||
AstConst* rwidth = rselp->widthp()->castConst();
|
||||
if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated
|
||||
int rend = (rstart->toSInt() + rwidth->toSInt());
|
||||
// a[i:j] a[j-1:k]
|
||||
if (rend == lstart->toSInt()) return true;
|
||||
// a[i:0] a[msb:j]
|
||||
if (rend == rfromp->width() && lstart->toSInt() == 0) return true;
|
||||
return false;
|
||||
}
|
||||
bool concatMergeable(AstNode* lhsp, AstNode* rhsp) {
|
||||
// determine if {a OP b, c OP d} => {a, c} OP {b, d} is advantagous
|
||||
if (!v3Global.opt.oAssemble()) return false; // opt disabled
|
||||
if (lhsp->type() != rhsp->type()) return false;
|
||||
if (!ifConcatMergeableBiop(lhsp)) return false;
|
||||
|
||||
AstNodeBiop* lp = lhsp->castNodeBiop();
|
||||
AstNodeBiop* rp = rhsp->castNodeBiop();
|
||||
if (!lp || !rp) return false;
|
||||
// {a[]&b[], a[]&b[]}
|
||||
bool lad = ifMergeAdjacent(lp->lhsp(), rp->lhsp());
|
||||
bool rad = ifMergeAdjacent(lp->rhsp(), rp->rhsp());
|
||||
if (lad && rad) return true;
|
||||
// {a[] & b[]&c[], a[] & b[]&c[]}
|
||||
else if (lad && concatMergeable(lp->rhsp(), rp->rhsp())) return true;
|
||||
// {a[]&b[] & c[], a[]&b[] & c[]}
|
||||
else if (rad && concatMergeable(lp->lhsp(), rp->lhsp())) return true;
|
||||
else {
|
||||
// {(a[]&b[])&(c[]&d[]), (a[]&b[])&(c[]&d[])}
|
||||
if (concatMergeable(lp->lhsp(), rp->lhsp())
|
||||
&& concatMergeable(lp->rhsp(), rp->rhsp()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Constant Replacement functions.
|
||||
@@ -538,6 +637,8 @@ private:
|
||||
void replaceWChild(AstNode* nodep, AstNode* childp) {
|
||||
// NODE(..., CHILD(...)) -> CHILD(...)
|
||||
childp->unlinkFrBackWithNext();
|
||||
// If replacing a SEL for example, the data type comes from the parent (is less wide).
|
||||
// This may adversly affect the operation of the node being replaced.
|
||||
childp->dtypeFrom(nodep);
|
||||
nodep->replaceWith(childp);
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
@@ -660,6 +761,45 @@ private:
|
||||
rrp->deleteTree();
|
||||
//nodep->dumpTree(cout, " repShiftSame_new: ");
|
||||
}
|
||||
void replaceConcatSel(AstConcat* nodep) {
|
||||
// {a[1], a[0]} -> a[1:0]
|
||||
AstSel* lselp = nodep->lhsp()->unlinkFrBack()->castSel();
|
||||
AstSel* rselp = nodep->rhsp()->unlinkFrBack()->castSel();
|
||||
int lstart = lselp->lsbConst();
|
||||
int lwidth = lselp->widthConst();
|
||||
int rstart = rselp->lsbConst();
|
||||
int rwidth = rselp->widthConst();
|
||||
|
||||
if ((rstart + rwidth) != lstart) nodep->v3fatalSrc("tried to merge two selects which are not adjacent");
|
||||
AstSel* newselp = new AstSel(lselp->fromp()->fileline(), rselp->fromp()->cloneTree(false), rstart, lwidth+rwidth);
|
||||
UINFO(5, "merged two adjacent sel "<<lselp <<" and "<<rselp<< " to one "<<newselp<<endl);
|
||||
|
||||
nodep->replaceWith(newselp);
|
||||
lselp->deleteTree(); lselp = NULL;
|
||||
rselp->deleteTree(); rselp = NULL;
|
||||
nodep->deleteTree(); nodep = NULL;
|
||||
}
|
||||
void replaceConcatMerge(AstConcat* nodep) {
|
||||
AstNodeBiop* lp = nodep->lhsp()->castNodeBiop();
|
||||
AstNodeBiop* rp = nodep->rhsp()->castNodeBiop();
|
||||
AstNode* llp = lp->lhsp()->cloneTree(false);
|
||||
AstNode* lrp = lp->rhsp()->cloneTree(false);
|
||||
AstNode* rlp = rp->lhsp()->cloneTree(false);
|
||||
AstNode* rrp = rp->rhsp()->cloneTree(false);
|
||||
if (concatMergeable(lp, rp)) {
|
||||
AstConcat* newlp = new AstConcat(rlp->fileline(), llp, rlp);
|
||||
AstConcat* newrp = new AstConcat(rrp->fileline(), lrp, rrp);
|
||||
// use the lhs to replace the parent concat
|
||||
lp->lhsp()->replaceWith(newlp);
|
||||
lp->rhsp()->replaceWith(newrp);
|
||||
lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), AstNumeric::fromBool(true));
|
||||
UINFO(5, "merged "<< nodep <<endl);
|
||||
rp->unlinkFrBack()->deleteTree(); rp = NULL;
|
||||
nodep->replaceWith(lp->unlinkFrBack()); nodep->deleteTree(); nodep = NULL;
|
||||
lp->lhsp()->accept(*this);
|
||||
lp->rhsp()->accept(*this);
|
||||
} else nodep->v3fatalSrc("tried to merge two Concat which are not adjacent");
|
||||
}
|
||||
void replaceExtend (AstNode* nodep, AstNode* arg0p) {
|
||||
// -> EXTEND(nodep)
|
||||
// like a AstExtend{$rhsp}, but we need to set the width correctly from base node
|
||||
@@ -722,9 +862,15 @@ private:
|
||||
AstNode* ap = lhsp->lhsp()->unlinkFrBack();
|
||||
AstNode* shift1p = lhsp->rhsp()->unlinkFrBack();
|
||||
AstNode* shift2p = nodep->rhsp()->unlinkFrBack();
|
||||
// Shift1p and shift2p may have different sizes, both are self-determined so sum with infinite width
|
||||
if (nodep->type()==lhsp->type()) {
|
||||
int shift1 = shift1p->castConst()->toUInt();
|
||||
int shift2 = shift2p->castConst()->toUInt();
|
||||
int newshift = shift1+shift2;
|
||||
shift1p->deleteTree(); shift1p=NULL;
|
||||
shift2p->deleteTree(); shift2p=NULL;
|
||||
nodep->lhsp(ap);
|
||||
nodep->rhsp(new AstAdd(nodep->fileline(), shift1p, shift2p));
|
||||
nodep->rhsp(new AstConst(nodep->fileline(), newshift));
|
||||
nodep->accept(*this); // Further reduce, either node may have more reductions.
|
||||
} else {
|
||||
// We know shift amounts are constant, but might be a mixed left/right shift
|
||||
@@ -732,7 +878,7 @@ private:
|
||||
int shift2 = shift2p->castConst()->toUInt(); if (nodep->castShiftR()) shift2=-shift2;
|
||||
int newshift = shift1+shift2;
|
||||
shift1p->deleteTree(); shift1p=NULL;
|
||||
shift2p->deleteTree(); shift1p=NULL;
|
||||
shift2p->deleteTree(); shift2p=NULL;
|
||||
AstNode* newp;
|
||||
V3Number mask1 (nodep->fileline(), nodep->width());
|
||||
V3Number ones (nodep->fileline(), nodep->width());
|
||||
@@ -778,7 +924,7 @@ private:
|
||||
AstSel* sel2p = nextp->lhsp()->castSel(); if (!sel2p) return false;
|
||||
AstVarRef* varref1p = sel1p->fromp()->castVarRef(); if (!varref1p) return false;
|
||||
AstVarRef* varref2p = sel2p->fromp()->castVarRef(); if (!varref2p) return false;
|
||||
if (!varref1p->sameTree(varref2p)) return false;
|
||||
if (!varref1p->sameGateTree(varref2p)) return false;
|
||||
AstConst* con1p = sel1p->lsbp()->castConst(); if (!con1p) return false;
|
||||
AstConst* con2p = sel2p->lsbp()->castConst(); if (!con2p) return false;
|
||||
// We need to make sure there's no self-references involved in either
|
||||
@@ -866,10 +1012,10 @@ private:
|
||||
}
|
||||
if (debug()>=9) nodep->dumpTree(cout," Ass_old: ");
|
||||
// Unlink the stuff
|
||||
AstNode* lc1p = nodep->lhsp()->castConcat()->lhsp()->unlinkFrBack();
|
||||
AstNode* lc2p = nodep->lhsp()->castConcat()->rhsp()->unlinkFrBack();
|
||||
AstNode* conp = nodep->lhsp()->castConcat()->unlinkFrBack();
|
||||
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstNode* lc1p = nodep->lhsp()->castConcat()->lhsp()->unlinkFrBack();
|
||||
AstNode* lc2p = nodep->lhsp()->castConcat()->rhsp()->unlinkFrBack();
|
||||
AstNode* conp = nodep->lhsp()->castConcat()->unlinkFrBack();
|
||||
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstNode* rhs2p = rhsp->cloneTree(false);
|
||||
// Calc widths
|
||||
int lsb2 = 0;
|
||||
@@ -938,6 +1084,64 @@ private:
|
||||
// Further reduce, either node may have more reductions.
|
||||
return true;
|
||||
}
|
||||
else if (m_doV && nodep->rhsp()->castStreamR()) {
|
||||
// The right-streaming operator on rhs of assignment does not
|
||||
// change the order of bits. Eliminate stream but keep its lhsp
|
||||
// Unlink the stuff
|
||||
AstNode* srcp = nodep->rhsp()->castStreamR()->lhsp()->unlinkFrBack();
|
||||
AstNode* sizep = nodep->rhsp()->castStreamR()->rhsp()->unlinkFrBack();
|
||||
AstNode* streamp = nodep->rhsp()->castStreamR()->unlinkFrBack();
|
||||
nodep->rhsp(srcp);
|
||||
// Cleanup
|
||||
sizep->deleteTree(); sizep=NULL;
|
||||
streamp->deleteTree(); streamp=NULL;
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
}
|
||||
else if (m_doV && nodep->lhsp()->castStreamL()) {
|
||||
// Push the stream operator to the rhs of the assignment statement
|
||||
int dWidth = nodep->lhsp()->castStreamL()->lhsp()->width();
|
||||
int sWidth = nodep->rhsp()->width();
|
||||
// Unlink the stuff
|
||||
AstNode* dstp = nodep->lhsp()->castStreamL()->lhsp()->unlinkFrBack();
|
||||
AstNode* streamp = nodep->lhsp()->castStreamL()->unlinkFrBack();
|
||||
AstNode* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
// Connect the rhs to the stream operator and update its width
|
||||
streamp->castStreamL()->lhsp(srcp);
|
||||
streamp->dtypeSetLogicSized((srcp->width()),
|
||||
(srcp->widthMin()),
|
||||
AstNumeric::UNSIGNED);
|
||||
// Shrink the RHS if necessary
|
||||
if (sWidth > dWidth) {
|
||||
streamp = new AstSel(streamp->fileline(), streamp, sWidth-dWidth, dWidth);
|
||||
}
|
||||
// Link the nodes back in
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(streamp);
|
||||
return true;
|
||||
}
|
||||
else if (m_doV && nodep->lhsp()->castStreamR()) {
|
||||
// The right stream operator on lhs of assignment statement does
|
||||
// not reorder bits. However, if the rhs is wider than the lhs,
|
||||
// then we select bits from the left-most, not the right-most.
|
||||
int dWidth = nodep->lhsp()->castStreamR()->lhsp()->width();
|
||||
int sWidth = nodep->rhsp()->width();
|
||||
// Unlink the stuff
|
||||
AstNode* dstp = nodep->lhsp()->castStreamR()->lhsp()->unlinkFrBack();
|
||||
AstNode* sizep = nodep->lhsp()->castStreamR()->rhsp()->unlinkFrBack();
|
||||
AstNode* streamp = nodep->lhsp()->castStreamR()->unlinkFrBack();
|
||||
AstNode* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
if (sWidth > dWidth) {
|
||||
srcp = new AstSel(streamp->fileline(), srcp, sWidth-dWidth, dWidth);
|
||||
}
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(srcp);
|
||||
// Cleanup
|
||||
sizep->deleteTree(); sizep=NULL;
|
||||
streamp->deleteTree(); streamp=NULL;
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
}
|
||||
else if (replaceAssignMultiSel(nodep)) {
|
||||
return true;
|
||||
}
|
||||
@@ -1219,15 +1423,35 @@ private:
|
||||
nodep->iterateChildren(*this);
|
||||
m_attrp = oldAttr;
|
||||
}
|
||||
|
||||
virtual void visit(AstArraySel* nodep, AstNUser*) {
|
||||
nodep->bitp()->iterateAndNext(*this);
|
||||
if (nodep->bitp()->castConst()
|
||||
&& nodep->fromp()->castVarRef()
|
||||
// Need to make sure it's an array object so don't mis-allow a constant (bug509.)
|
||||
&& nodep->fromp()->castVarRef()->varp()
|
||||
&& nodep->fromp()->castVarRef()->varp()->valuep()->castInitArray()) {
|
||||
m_selp = nodep; // Ask visit(AstVarRef) to replace varref with const
|
||||
}
|
||||
nodep->fromp()->iterateAndNext(*this);
|
||||
if (nodep->fromp()->castConst()) { // It did.
|
||||
if (!m_selp) {
|
||||
nodep->v3error("Illegal assignment of constant to unpacked array");
|
||||
} else {
|
||||
nodep->replaceWith(nodep->fromp()->unlinkFrBack());
|
||||
}
|
||||
}
|
||||
m_selp = NULL;
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
if (!nodep->varp()) nodep->v3fatalSrc("Not linked");
|
||||
bool did=false;
|
||||
if (m_doV && nodep->varp()->hasSimpleInit() && !m_attrp) {
|
||||
//if (debug()) nodep->varp()->valuep()->dumpTree(cout," visitvaref: ");
|
||||
nodep->varp()->valuep()->iterateAndNext(*this);
|
||||
if (operandConst(nodep->varp()->valuep())
|
||||
&& !nodep->lvalue()
|
||||
if (m_doV && nodep->varp()->valuep() && !m_attrp) {
|
||||
//if (debug()) valuep->dumpTree(cout," visitvaref: ");
|
||||
nodep->varp()->valuep()->iterateAndNext(*this); // May change nodep->varp()->valuep()
|
||||
AstNode* valuep = nodep->varp()->valuep();
|
||||
if (!nodep->lvalue()
|
||||
&& ((!m_params // Can reduce constant wires into equations
|
||||
&& m_doNConst
|
||||
&& v3Global.opt.oConst()
|
||||
@@ -1235,11 +1459,23 @@ private:
|
||||
&& nodep->varp()->isInput())
|
||||
&& !nodep->varp()->isSigPublic())
|
||||
|| nodep->varp()->isParam())) {
|
||||
AstConst* constp = nodep->varp()->valuep()->castConst();
|
||||
const V3Number& num = constp->num();
|
||||
//UINFO(2,"constVisit "<<(void*)constp<<" "<<num<<endl);
|
||||
replaceNum(nodep, num); nodep=NULL;
|
||||
did=true;
|
||||
if (operandConst(valuep)) {
|
||||
const V3Number& num = valuep->castConst()->num();
|
||||
//UINFO(2,"constVisit "<<(void*)valuep<<" "<<num<<endl);
|
||||
replaceNum(nodep, num); nodep=NULL;
|
||||
did=true;
|
||||
}
|
||||
else if (m_selp && valuep->castInitArray()) {
|
||||
int bit = m_selp->bitConst();
|
||||
AstNode* itemp = valuep->castInitArray()->initsp();
|
||||
for (int n=0; n<bit && itemp; ++n, itemp=itemp->nextp()) {}
|
||||
if (itemp->castConst()) {
|
||||
const V3Number& num = itemp->castConst()->num();
|
||||
//UINFO(2,"constVisit "<<(void*)valuep<<" "<<num<<endl);
|
||||
replaceNum(nodep, num); nodep=NULL;
|
||||
did=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!did && m_required) {
|
||||
@@ -1435,7 +1671,7 @@ private:
|
||||
AstSenItem* litemp = senp->castSenItem();
|
||||
AstSenItem* ritemp = cmpp->castSenItem();
|
||||
if (litemp && ritemp) {
|
||||
if ((litemp->varrefp() && ritemp->varrefp() && litemp->varrefp()->sameTree(ritemp->varrefp()))
|
||||
if ((litemp->varrefp() && ritemp->varrefp() && litemp->varrefp()->sameGateTree(ritemp->varrefp()))
|
||||
|| (!litemp->varrefp() && !ritemp->varrefp())) {
|
||||
// We've sorted in the order ANY, BOTH, POS, NEG, so we don't need to try opposite orders
|
||||
if (( litemp->edgeType()==AstEdgeType::ET_ANYEDGE) // ANY or {BOTH|POS|NEG} -> ANY
|
||||
@@ -1592,8 +1828,9 @@ private:
|
||||
string fmt = "";
|
||||
bool inPct = false;
|
||||
AstNode* argp = nodep->exprsp();
|
||||
for (const char* inp = nodep->text().c_str(); *inp; inp++) {
|
||||
char ch = *inp; // Breaks with iterators...
|
||||
string text = nodep->text();
|
||||
for (string::const_iterator it = text.begin(); it!=text.end(); ++it) {
|
||||
char ch = *it;
|
||||
if (!inPct && ch=='%') {
|
||||
inPct = true;
|
||||
fmt = ch;
|
||||
@@ -1664,6 +1901,10 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstInitArray* nodep, AstNUser*) {
|
||||
// Constant if all children are constant
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
// These are converted by V3Param. Don't constify as we don't want the from() VARREF to disappear, if any
|
||||
// If output of a presel didn't get consted, chances are V3Param didn't visit properly
|
||||
@@ -1709,6 +1950,12 @@ private:
|
||||
|
||||
//-----
|
||||
// Below lines are magic expressions processed by astgen
|
||||
// TREE_SKIP_VISIT("AstNODETYPE") # Rename normal visit to visitGen and don't iterate
|
||||
//-----
|
||||
|
||||
TREE_SKIP_VISIT("ArraySel");
|
||||
|
||||
//-----
|
||||
// "AstNODETYPE { # bracket not paren
|
||||
// $accessor_name, ...
|
||||
// # ,, gets replaced with a , rather than &&
|
||||
@@ -1749,8 +1996,14 @@ private:
|
||||
TREEOP ("AstDivS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
||||
TREEOP ("AstMul {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
||||
TREEOP ("AstMulS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
||||
TREEOP ("AstPow {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
||||
TREEOP ("AstPowS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
||||
TREEOP ("AstPow {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
|
||||
TREEOP ("AstPowSS {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
|
||||
TREEOP ("AstPowSU {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
|
||||
TREEOP ("AstPowUS {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule
|
||||
TREEOP ("AstPow {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
|
||||
TREEOP ("AstPowSU {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
|
||||
TREEOP ("AstPowUS {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
|
||||
TREEOP ("AstPowSU {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)");
|
||||
TREEOP ("AstOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
||||
TREEOP ("AstShiftL{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
||||
TREEOP ("AstShiftR{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)");
|
||||
@@ -1789,7 +2042,7 @@ private:
|
||||
TREEOP ("AstMul {operandIsPowTwo($lhsp), $rhsp}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
|
||||
TREEOP ("AstDiv {$lhsp, operandIsPowTwo($rhsp)}", "replaceDivShift(nodep)"); // a/2^n -> a>>n
|
||||
TREEOP ("AstPow {operandIsTwo($lhsp), $rhsp}", "replacePowShift(nodep)"); // 2**a == 1<<a
|
||||
TREEOP ("AstPowS {operandIsTwo($lhsp), $rhsp}", "replacePowShift(nodep)"); // 2**a == 1<<a
|
||||
TREEOP ("AstSub {$lhsp.castAdd, operandSubAdd(nodep)}", "AstAdd{AstSub{$lhsp->castAdd()->lhsp(),$rhsp}, $lhsp->castAdd()->rhsp()}"); // ((a+x)-y) -> (a+(x-y))
|
||||
// Trinary ops
|
||||
// Note V3Case::Sel requires Cond to always be conditionally executed in C to prevent core dump!
|
||||
TREEOP ("AstNodeCond{$condp.isZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr2p)");
|
||||
@@ -1812,6 +2065,14 @@ private:
|
||||
TREEOP ("AstNodeBiComAsv{operandAsvSame(nodep)}", "replaceAsv(nodep)");
|
||||
TREEOP ("AstNodeBiComAsv{operandAsvLUp(nodep)}", "replaceAsvLUp(nodep)");
|
||||
TREEOP ("AstNodeBiComAsv{operandAsvRUp(nodep)}", "replaceAsvRUp(nodep)");
|
||||
TREEOP ("AstLt {!$lhsp.castConst,$rhsp.castConst}", "AstGt {$rhsp,$lhsp}");
|
||||
TREEOP ("AstLtS {!$lhsp.castConst,$rhsp.castConst}", "AstGtS {$rhsp,$lhsp}");
|
||||
TREEOP ("AstLte {!$lhsp.castConst,$rhsp.castConst}", "AstGte {$rhsp,$lhsp}");
|
||||
TREEOP ("AstLteS {!$lhsp.castConst,$rhsp.castConst}", "AstGteS{$rhsp,$lhsp}");
|
||||
TREEOP ("AstGt {!$lhsp.castConst,$rhsp.castConst}", "AstLt {$rhsp,$lhsp}");
|
||||
TREEOP ("AstGtS {!$lhsp.castConst,$rhsp.castConst}", "AstLtS {$rhsp,$lhsp}");
|
||||
TREEOP ("AstGte {!$lhsp.castConst,$rhsp.castConst}", "AstLte {$rhsp,$lhsp}");
|
||||
TREEOP ("AstGteS {!$lhsp.castConst,$rhsp.castConst}", "AstLteS{$rhsp,$lhsp}");
|
||||
// v--- *1* as These ops are always first, as we warn before replacing
|
||||
TREEOP1("AstLt {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,0)");
|
||||
TREEOP1("AstGte {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,1)");
|
||||
@@ -1823,7 +2084,7 @@ private:
|
||||
TREEOP1("AstGte {$lhsp.isAllOnes, $rhsp, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,1)");
|
||||
// Two level bubble pushing
|
||||
TREEOP ("AstNot {$lhsp.castNot, $lhsp->width()==$lhsp->castNot()->lhsp()->width()}", "replaceWChild(nodep, $lhsp->op1p())"); // NOT(NOT(x))->x
|
||||
TREEOP ("AstLogNot{$lhsp.castLogNot}", "replaceWChild(nodep, $lhsp->op1p())"); // NOT(NOT(x))->x
|
||||
TREEOP ("AstLogNot{$lhsp.castLogNot}", "replaceWChild(nodep, $lhsp->op1p())"); // LOGNOT(LOGNOT(x))->x
|
||||
TREEOPV("AstNot {$lhsp.castEqCase, $lhsp.width1}","AstNeqCase{$lhsp->op1p(),$lhsp->op2p()}");
|
||||
TREEOP ("AstLogNot{$lhsp.castEqCase}", "AstNeqCase{$lhsp->op1p(),$lhsp->op2p()}");
|
||||
TREEOPV("AstNot {$lhsp.castNeqCase, $lhsp.width1}","AstEqCase {$lhsp->op1p(),$lhsp->op2p()}");
|
||||
@@ -1861,12 +2122,12 @@ private:
|
||||
TREEOP ("AstShiftR{operandShiftShift(nodep)}", "replaceShiftShift(nodep)");
|
||||
TREEOP ("AstWordSel{operandWordOOB(nodep)}", "replaceZero(nodep)");
|
||||
// Compress out EXTENDs to appease loop unroller
|
||||
TREEOPV("AstEq {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
TREEOPV("AstNeq {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
TREEOPV("AstGt {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
TREEOPV("AstGte {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
TREEOPV("AstLt {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
TREEOPV("AstLte {$lhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
TREEOPV("AstEq {$rhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
TREEOPV("AstNeq {$rhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
TREEOPV("AstGt {$rhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
TREEOPV("AstGte {$rhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
TREEOPV("AstLt {$rhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
TREEOPV("AstLte {$rhsp.castExtend,operandBiExtendConst(nodep)}", "DONE");
|
||||
// Identical operands on both sides
|
||||
// AstLogAnd/AstLogOr already converted to AstAnd/AstOr for these rules
|
||||
// AstAdd->ShiftL(#,1) but uncommon
|
||||
@@ -1936,6 +2197,9 @@ private:
|
||||
// CONCAT({const},CONCAT({const},{c})) -> CONCAT((constifiedCONC{const|const},{c}))
|
||||
TREEOPV("AstConcat{operandConcatMove(nodep)}", "moveConcat(nodep)");
|
||||
TREEOPV("AstConcat{$lhsp.isZero, $rhsp}", "replaceExtend(nodep, nodep->rhsp())");
|
||||
// CONCAT(a[1],a[0]) -> a[1:0]
|
||||
TREEOPV("AstConcat{$lhsp->castSel(), $rhsp->castSel(), ifAdjacentSel($lhsp->castSel(),,$rhsp->castSel())}", "replaceConcatSel(nodep)");
|
||||
TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp)}", "replaceConcatMerge(nodep)");
|
||||
// Common two-level operations that can be simplified
|
||||
TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
|
||||
TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
|
||||
@@ -1943,7 +2207,7 @@ private:
|
||||
TREEOP ("AstAnd {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
||||
TREEOP ("AstOr {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
||||
TREEOP ("AstXor {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
||||
TREEOP ("AstXnor{operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
||||
// "AstXnor{operandShiftSame(nodep)}", // Cannot ShiftSame as the shifted-in zeros might create a one
|
||||
// Note can't simplify a extend{extends}, extends{extend}, as the sign bits end up in the wrong places
|
||||
TREEOPV("AstExtend {$lhsp.castExtend}", "replaceExtend(nodep, nodep->lhsp()->castExtend()->lhsp())");
|
||||
TREEOPV("AstExtendS{$lhsp.castExtendS}", "replaceExtend(nodep, nodep->lhsp()->castExtendS()->lhsp())");
|
||||
@@ -2028,6 +2292,7 @@ public:
|
||||
m_warn = false;
|
||||
m_wremove = true; // Overridden in visitors
|
||||
m_modp = NULL;
|
||||
m_selp = NULL;
|
||||
m_scopep = NULL;
|
||||
m_attrp = NULL;
|
||||
//
|
||||
|
||||
+17
-8
@@ -60,9 +60,14 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstIf::user1() -> bool. True indicates ifelse processed
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
bool m_checkBlock; // Should this block get covered?
|
||||
AstNodeModule* m_modp; // Current module to add statement to
|
||||
bool m_checkBlock; // Should this block get covered?
|
||||
AstNodeModule* m_modp; // Current module to add statement to
|
||||
bool m_inToggleOff; // In function/task etc
|
||||
bool m_inModOff; // In module with no coverage
|
||||
FileMap m_fileps; // Column counts for each fileline
|
||||
@@ -277,12 +282,18 @@ private:
|
||||
virtual void visit(AstIf* nodep, AstNUser*) { // Note not AstNodeIf; other types don't get covered
|
||||
UINFO(4," IF: "<<nodep<<endl);
|
||||
if (m_checkBlock) {
|
||||
// An else-if. When we iterate the if, use "elsif" marking
|
||||
bool elsif = (nodep->elsesp()->castIf()
|
||||
&& !nodep->elsesp()->castIf()->nextp());
|
||||
if (elsif) nodep->elsesp()->castIf()->user1(true);
|
||||
//
|
||||
nodep->ifsp()->iterateAndNext(*this);
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it
|
||||
if (!nodep->backp()->castIf()
|
||||
|| nodep->backp()->castIf()->elsesp()!=nodep) { // Ignore if else; did earlier
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
if (nodep->user1()) {
|
||||
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "elsif"));
|
||||
} else {
|
||||
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "if"));
|
||||
}
|
||||
}
|
||||
@@ -293,9 +304,7 @@ private:
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
if (nodep->elsesp()->castIf()) {
|
||||
nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(), "", "v_line", "elsif"));
|
||||
} else {
|
||||
if (!elsif) { // elsif done inside if()
|
||||
nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(), "", "v_line", "else"));
|
||||
}
|
||||
}
|
||||
|
||||
+19
-1
@@ -81,6 +81,7 @@ private:
|
||||
typedef multimap<AstVarScope*,AstNodeAssign*> AssignMap;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
vector<AstNode*> m_varEtcsp; // List of all encountered to avoid another loop through tree
|
||||
vector<AstVarScope*> m_vscsp; // List of all encountered to avoid another loop through tree
|
||||
AssignMap m_assignMap; // List of all simple assignments for each variable
|
||||
@@ -112,6 +113,12 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
checkAll(nodep);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstCell* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
checkAll(nodep);
|
||||
@@ -158,6 +165,13 @@ private:
|
||||
nodep->packagep()->user1Inc();
|
||||
}
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
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();
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
checkAll(nodep);
|
||||
@@ -168,6 +182,7 @@ private:
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
checkAll(nodep);
|
||||
if (nodep->isSigPublic() && m_modp && m_modp->castPackage()) m_modp->user1Inc();
|
||||
if (mightElim(nodep)) {
|
||||
m_varEtcsp.push_back(nodep);
|
||||
}
|
||||
@@ -223,7 +238,9 @@ private:
|
||||
bool mightElim(AstVar* nodep) {
|
||||
return (!nodep->isSigPublic() // Can't elim publics!
|
||||
&& !nodep->isIO()
|
||||
&& (nodep->isTemp() || nodep->isParam() || m_elimUserVars));
|
||||
&& (nodep->isTemp()
|
||||
|| (nodep->isParam() && !nodep->isTrace())
|
||||
|| m_elimUserVars)); // Post-Trace can kill most anything
|
||||
}
|
||||
void deadCheckVar() {
|
||||
// Delete any unused varscopes
|
||||
@@ -251,6 +268,7 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes) {
|
||||
m_modp = NULL;
|
||||
m_elimUserVars = elimUserVars;
|
||||
m_elimDTypes = elimDTypes;
|
||||
m_sideEffect = false;
|
||||
|
||||
+9
-7
@@ -118,7 +118,7 @@ private:
|
||||
nodep->v3warn(BLKANDNBLK,"Unsupported: Blocked and non-blocking assignments to same variable: "<<nodep->varp()->prettyName());
|
||||
}
|
||||
}
|
||||
AstVarScope* createVarSc(AstVarScope* oldvarscp, string name, int width/*0==fromoldvar*/) {
|
||||
AstVarScope* createVarSc(AstVarScope* oldvarscp, string name, int width/*0==fromoldvar*/, AstNodeDType* newdtypep) {
|
||||
// Because we've already scoped it, we may need to add both the AstVar and the AstVarScope
|
||||
if (!oldvarscp->scopep()) oldvarscp->v3fatalSrc("Var unscoped");
|
||||
AstVar* varp;
|
||||
@@ -129,7 +129,9 @@ private:
|
||||
// Created module's AstVar earlier under some other scope
|
||||
varp = it->second;
|
||||
} else {
|
||||
if (width==0) {
|
||||
if (newdtypep) {
|
||||
varp = new AstVar (oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, newdtypep);
|
||||
} else if (width==0) {
|
||||
varp = new AstVar (oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, oldvarscp->varp());
|
||||
varp->dtypeFrom(oldvarscp);
|
||||
} else { // Used for vset and dimensions, so can zero init
|
||||
@@ -219,7 +221,7 @@ private:
|
||||
} else {
|
||||
string bitvarname = (string("__Vdlyvdim")+cvtToStr(dimension)
|
||||
+"__"+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, dimp->width());
|
||||
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, dimp->width(), NULL);
|
||||
AstAssign* bitassignp
|
||||
= new AstAssign (nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), bitvscp, true),
|
||||
@@ -237,7 +239,7 @@ private:
|
||||
bitreadp = lsbvaluep;
|
||||
} else {
|
||||
string bitvarname = (string("__Vdlyvlsb__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width());
|
||||
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width(), NULL);
|
||||
AstAssign* bitassignp = new AstAssign (nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), bitvscp, true),
|
||||
lsbvaluep);
|
||||
@@ -252,7 +254,7 @@ private:
|
||||
valreadp = nodep->rhsp()->unlinkFrBack();
|
||||
} else {
|
||||
string valvarname = (string("__Vdlyvval__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* valvscp = createVarSc(varrefp->varScopep(), valvarname, nodep->rhsp()->width());
|
||||
AstVarScope* valvscp = createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep());
|
||||
newlhsp = new AstVarRef(nodep->fileline(), valvscp, true);
|
||||
valreadp = new AstVarRef(nodep->fileline(), valvscp, false);
|
||||
}
|
||||
@@ -270,7 +272,7 @@ private:
|
||||
++m_statSharedSet;
|
||||
} else { // Create new one
|
||||
string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
setvscp = createVarSc(varrefp->varScopep(), setvarname, 1);
|
||||
setvscp = createVarSc(varrefp->varScopep(), setvarname, 1, NULL);
|
||||
setinitp = new AstAssignPre (nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), setvscp, true),
|
||||
new AstConst(nodep->fileline(), 0));
|
||||
@@ -399,7 +401,7 @@ private:
|
||||
}
|
||||
if (!dlyvscp) { // First use of this delayed variable
|
||||
string newvarname = (string("__Vdly__")+nodep->varp()->shortName());
|
||||
dlyvscp = createVarSc(oldvscp, newvarname, 0);
|
||||
dlyvscp = createVarSc(oldvscp, newvarname, 0, NULL);
|
||||
AstNodeAssign* prep
|
||||
= new AstAssignPre (nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), dlyvscp, true),
|
||||
|
||||
+115
-38
@@ -32,6 +32,7 @@
|
||||
#include "V3String.h"
|
||||
#include "V3EmitC.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Number.h"
|
||||
|
||||
#define VL_VALUE_STRING_MAX_WIDTH 8192 // We use a static char array in VL_VALUE_STRING
|
||||
|
||||
@@ -71,7 +72,7 @@ public:
|
||||
string vfmt, char fmtLetter);
|
||||
|
||||
void emitVarDecl(AstVar* nodep, const string& prefixIfImp);
|
||||
typedef enum {EVL_IO, EVL_SIG, EVL_TEMP, EVL_STATIC, EVL_ALL} EisWhich;
|
||||
typedef enum {EVL_IO, EVL_SIG, EVL_TEMP, EVL_PAR, EVL_ALL} EisWhich;
|
||||
void emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp);
|
||||
void emitVarCtors();
|
||||
bool emitSimpleOk(AstNodeMath* nodep);
|
||||
@@ -95,6 +96,38 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void emitTypedefs(AstNode* firstp) {
|
||||
bool first = true;
|
||||
for (AstNode* loopp=firstp; loopp; loopp = loopp->nextp()) {
|
||||
if (AstTypedef* nodep = loopp->castTypedef()) {
|
||||
if (nodep->attrPublic()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
puts("\n// TYPEDEFS\n");
|
||||
puts("// That were declared public\n");
|
||||
} else {
|
||||
puts("\n");
|
||||
}
|
||||
if (AstEnumDType* adtypep = nodep->dtypep()->skipRefToEnump()->castEnumDType()) {
|
||||
if (adtypep->width()>64) {
|
||||
puts("// enum "+nodep->name()+" // Ignored: Too wide for C++\n");
|
||||
} else {
|
||||
puts("enum "+nodep->name()+" {\n");
|
||||
for (AstEnumItem* itemp = adtypep->itemsp(); itemp; itemp=itemp->nextp()->castEnumItem()) {
|
||||
puts(itemp->name());
|
||||
puts(" = ");
|
||||
itemp->valuep()->iterateAndNext(*this);
|
||||
if (nodep->nextp()) puts(",");
|
||||
puts("\n");
|
||||
}
|
||||
puts("};\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
bool paren = true; bool decind = false;
|
||||
@@ -225,7 +258,9 @@ public:
|
||||
virtual void visit(AstScopeName* nodep, AstNUser*) {
|
||||
// For use under AstCCalls for dpiImports. ScopeNames under displays are handled in AstDisplay
|
||||
if (!nodep->dpiExport()) {
|
||||
putbs("(&(vlSymsp->__Vscope_"+nodep->scopeSymName()+"))");
|
||||
// this is where the DPI import context scope is set
|
||||
string scope = nodep->scopeDpiName();
|
||||
putbs("(&(vlSymsp->__Vscope_"+scope+"))");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstSFormat* nodep, AstNUser*) {
|
||||
@@ -245,12 +280,14 @@ public:
|
||||
char format = '?';
|
||||
bool pct=false;
|
||||
int got=0;
|
||||
for (const char* cp = nodep->text().c_str(); *cp; cp++) {
|
||||
string txt = nodep->text();
|
||||
for (string::const_iterator it=txt.begin(); it!=txt.end(); ++it) {
|
||||
char ch = *it;
|
||||
if (pct) {
|
||||
pct = false;
|
||||
switch (tolower(*cp)) {
|
||||
switch (tolower(ch)) {
|
||||
case '%':
|
||||
prefix += *cp;
|
||||
prefix += ch;
|
||||
break;
|
||||
case 'd': // FALLTHRU
|
||||
case 'o': // FALLTHRU
|
||||
@@ -258,22 +295,22 @@ public:
|
||||
case 'x': // FALLTHRU
|
||||
case 'b': // FALLTHRU
|
||||
case 's':
|
||||
got++; format = tolower(*cp);
|
||||
got++; format = tolower(ch);
|
||||
break;
|
||||
case 'e': // FALLTHRU
|
||||
case 'f': // FALLTHRU
|
||||
case 'g':
|
||||
got++; format = tolower(*cp);
|
||||
nodep->v3error("Unsupported $value$plusargs format qualifier: '"<<*cp<<"'"<<endl);
|
||||
got++; format = tolower(ch);
|
||||
nodep->v3error("Unsupported $value$plusargs format qualifier: '"<<ch<<"'"<<endl);
|
||||
break;
|
||||
default:
|
||||
got++;
|
||||
nodep->v3error("Illegal $value$plusargs format qualifier: '"<<*cp<<"'"<<endl);
|
||||
nodep->v3error("Illegal $value$plusargs format qualifier: '"<<ch<<"'"<<endl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (*cp == '%') pct = true;
|
||||
else prefix += *cp;
|
||||
else if (ch == '%') pct = true;
|
||||
else prefix += ch;
|
||||
}
|
||||
if (got!=1) nodep->v3error("Missing or extra $value$plusargs format qualifier: '"<<nodep->text()<<"'"<<endl);
|
||||
puts("VL_VALUEPLUSARGS_I");
|
||||
@@ -552,6 +589,28 @@ public:
|
||||
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstStreamL* nodep, AstNUser*) {
|
||||
// Attempt to use a "fast" stream function for slice size = power of 2
|
||||
if (!nodep->isWide()) {
|
||||
uint32_t isPow2 = nodep->rhsp()->castConst()->num().countOnes() == 1;
|
||||
uint32_t sliceSize = nodep->rhsp()->castConst()->toUInt();
|
||||
if (isPow2 && sliceSize <= (nodep->isQuad() ? sizeof(uint64_t) : sizeof(uint32_t))) {
|
||||
puts("VL_STREAML_FAST_");
|
||||
emitIQW(nodep);
|
||||
emitIQW(nodep->lhsp());
|
||||
puts("I(");
|
||||
puts(cvtToStr(nodep->widthMin()));
|
||||
puts(","+cvtToStr(nodep->lhsp()->widthMin()));
|
||||
puts(","+cvtToStr(nodep->rhsp()->widthMin()));
|
||||
puts(",");
|
||||
nodep->lhsp()->iterateAndNext(*this); puts(", ");
|
||||
uint32_t rd_log2 = V3Number::log2b(nodep->rhsp()->castConst()->toUInt());
|
||||
puts(cvtToStr(rd_log2)+")");
|
||||
return;
|
||||
}
|
||||
}
|
||||
emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)", nodep->lhsp(), nodep->rhsp(), NULL);
|
||||
}
|
||||
// Terminals
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
puts(nodep->hiername());
|
||||
@@ -581,7 +640,15 @@ public:
|
||||
}
|
||||
ofp()->printf(",0x%08" VL_PRI64 "x)", (vluint64_t)(nodep->num().dataWord(0)));
|
||||
} else if (nodep->isDouble()) {
|
||||
ofp()->printf("%.17g", nodep->num().toDouble());
|
||||
if (int(nodep->num().toDouble()) == nodep->num().toDouble()
|
||||
&& nodep->num().toDouble() < 1000
|
||||
&& nodep->num().toDouble() > -1000) {
|
||||
ofp()->printf("%3.1f", nodep->num().toDouble()); // Force decimal point
|
||||
} else {
|
||||
// Not %g as will not always put in decimal point, so not obvious to compiler
|
||||
// is a real number
|
||||
ofp()->printf("%.17e", nodep->num().toDouble());
|
||||
}
|
||||
} else if (nodep->isQuad()) {
|
||||
vluint64_t num = nodep->toUQuad();
|
||||
if (num<10) ofp()->printf("VL_ULL(%" VL_PRI64 "d)", num);
|
||||
@@ -591,8 +658,9 @@ public:
|
||||
// Only 32 bits - llx + long long here just to appease CPP format warning
|
||||
if (num<10) puts(cvtToStr(num));
|
||||
else ofp()->printf("0x%" VL_PRI64 "x", (vluint64_t)num);
|
||||
//Unneeded-Causes %lx format warnings:
|
||||
// if (!nodep->num().isSigned() && (num & (1UL<<31))) puts("U");
|
||||
// If signed, we'll do our own functions
|
||||
// But must be here, or <= comparisons etc may end up signed
|
||||
puts("U");
|
||||
}
|
||||
}
|
||||
void emitSetVarConstant(const string& assignString, AstConst* constp) {
|
||||
@@ -627,6 +695,7 @@ public:
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
// NOPs
|
||||
virtual void visit(AstTypedef*, AstNUser*) {}
|
||||
virtual void visit(AstPragma*, AstNUser*) {}
|
||||
virtual void visit(AstCell*, AstNUser*) {} // Handled outside the Visit class
|
||||
virtual void visit(AstVar*, AstNUser*) {} // Handled outside the Visit class
|
||||
@@ -634,7 +703,6 @@ public:
|
||||
virtual void visit(AstTraceDecl*, AstNUser*) {} // Handled outside the Visit class
|
||||
virtual void visit(AstTraceInc*, AstNUser*) {} // Handled outside the Visit class
|
||||
virtual void visit(AstCFile*, AstNUser*) {} // Handled outside the Visit class
|
||||
virtual void visit(AstTypedef*, AstNUser*) {} // Nothing needed presently
|
||||
// Default
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
puts((string)"\n???? // "+nodep->prettyTypeName()+"\n");
|
||||
@@ -690,7 +758,8 @@ class EmitCImp : EmitCStmts {
|
||||
}
|
||||
changep->lhsp()->iterateAndNext(*this);
|
||||
if (changep->lhsp()->isWide()) puts("["+cvtToStr(word)+"]");
|
||||
puts(" ^ ");
|
||||
if (changep->lhsp()->isDouble()) puts(" != ");
|
||||
else puts(" ^ ");
|
||||
changep->rhsp()->iterateAndNext(*this);
|
||||
if (changep->lhsp()->isWide()) puts("["+cvtToStr(word)+"]");
|
||||
puts(")");
|
||||
@@ -748,6 +817,7 @@ class EmitCImp : EmitCStmts {
|
||||
|
||||
//---------------------------------------
|
||||
// VISITORS
|
||||
using EmitCStmts::visit; // Suppress hidden overloaded virtual function warnng
|
||||
virtual void visit(AstCFunc* nodep, AstNUser*) {
|
||||
// TRACE_* and DPI handled elsewhere
|
||||
if (nodep->funcType().isTrace()) return;
|
||||
@@ -786,9 +856,7 @@ class EmitCImp : EmitCStmts {
|
||||
|
||||
if (nodep->stmtsp()) puts("// Body\n");
|
||||
nodep->stmtsp()->iterateAndNext(*this);
|
||||
#ifndef NEW_ORDERING
|
||||
if (!m_blkChangeDetVec.empty()) emitChangeDet();
|
||||
#endif
|
||||
|
||||
if (nodep->finalsp()) puts("// Final\n");
|
||||
nodep->finalsp()->iterateAndNext(*this);
|
||||
@@ -802,7 +870,7 @@ class EmitCImp : EmitCStmts {
|
||||
|
||||
void emitChangeDet() {
|
||||
puts("// Change detection\n");
|
||||
puts("IData __req = false; // Logically a bool\n"); // But not because it results in faster code
|
||||
puts("QData __req = false; // Logically a bool\n"); // But not because it results in faster code
|
||||
bool gotOne = false;
|
||||
for (vector<AstChangeDet*>::iterator it = m_blkChangeDetVec.begin();
|
||||
it != m_blkChangeDetVec.end(); ++it) {
|
||||
@@ -900,6 +968,11 @@ void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
|
||||
puts(nodep->name());
|
||||
emitDeclArrayBrackets(nodep);
|
||||
puts(";\n");
|
||||
} else if (basicp && basicp->isOpaque()) {
|
||||
// strings and other fundamental c types; no VL_ macro can be used
|
||||
puts(nodep->vlArgType(true,false,false));
|
||||
emitDeclArrayBrackets(nodep);
|
||||
puts(";\n");
|
||||
} else { // C++ signals
|
||||
ofp()->putAlign(nodep->isStatic(), nodep->dtypeSkipRefp()->widthAlignBytes(),
|
||||
nodep->dtypeSkipRefp()->widthTotalBytes());
|
||||
@@ -923,7 +996,7 @@ void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
|
||||
}
|
||||
} else if (basicp && basicp->isOpaque()) {
|
||||
// strings and other fundamental c types
|
||||
puts(nodep->vlArgType(true,false));
|
||||
puts(nodep->vlArgType(true,false,false));
|
||||
emitDeclArrayBrackets(nodep);
|
||||
puts(";\n");
|
||||
} else {
|
||||
@@ -1074,9 +1147,13 @@ void EmitCStmts::emitOpName(AstNode* nodep, const string& format,
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (pos[0] == ')') {
|
||||
nextComma=""; puts(")");
|
||||
} else if (pos[0] == '(') {
|
||||
COMMA; needComma = false; puts("(");
|
||||
} else {
|
||||
// Normal text
|
||||
if (pos[0] == ')') nextComma="";
|
||||
if (isalnum(pos[0])) needComma = true;
|
||||
COMMA;
|
||||
string s; s+=pos[0]; puts(s);
|
||||
}
|
||||
@@ -1310,7 +1387,9 @@ void EmitCImp::emitVarResets(AstNodeModule* modp) {
|
||||
// Constructor deals with it
|
||||
}
|
||||
else if (varp->isParam()) {
|
||||
if (!varp->hasSimpleInit()) nodep->v3fatalSrc("No init for a param?");
|
||||
if (!varp->valuep()) nodep->v3fatalSrc("No init for a param?");
|
||||
// If a simple CONST value we initialize it using an enum
|
||||
// If an ARRAYINIT we initialize it using an initial block similar to a signal
|
||||
//puts("// parameter "+varp->name()+" = "+varp->valuep()->name()+"\n");
|
||||
}
|
||||
else if (AstInitArray* initarp = varp->valuep()->castInitArray()) {
|
||||
@@ -1340,7 +1419,7 @@ void EmitCImp::emitVarResets(AstNodeModule* modp) {
|
||||
}
|
||||
bool zeroit = (varp->attrFileDescr() // Zero it out, so we don't core dump if never call $fopen
|
||||
|| (varp->basicp() && varp->basicp()->isZeroInit())
|
||||
|| (varp->name().c_str()[0]=='_' && v3Global.opt.underlineZero()));
|
||||
|| (varp->name().size()>=1 && varp->name()[0]=='_' && v3Global.opt.underlineZero()));
|
||||
if (varp->isWide()) {
|
||||
// DOCUMENT: We randomize everything. If the user wants a _var to be zero,
|
||||
// there should be a initial statement. (Different from verilator2.)
|
||||
@@ -1621,20 +1700,16 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) {
|
||||
}
|
||||
puts("// Evaluate till stable\n");
|
||||
puts("VL_DEBUG_IF(VL_PRINTF(\"\\n----TOP Evaluate "+modClassName(modp)+"::eval\\n\"); );\n");
|
||||
#ifndef NEW_ORDERING
|
||||
puts("int __VclockLoop = 0;\n");
|
||||
puts("IData __Vchange=1;\n");
|
||||
puts("QData __Vchange=1;\n");
|
||||
puts("while (VL_LIKELY(__Vchange)) {\n");
|
||||
puts( "VL_DEBUG_IF(VL_PRINTF(\" Clock loop\\n\"););\n");
|
||||
#endif
|
||||
puts( "vlSymsp->__Vm_activity = true;\n");
|
||||
puts( "_eval(vlSymsp);\n");
|
||||
#ifndef NEW_ORDERING
|
||||
puts( "__Vchange = _change_request(vlSymsp);\n");
|
||||
puts( "if (++__VclockLoop > "+cvtToStr(v3Global.opt.convergeLimit())
|
||||
+") vl_fatal(__FILE__,__LINE__,__FILE__,\"Verilated model didn't converge\");\n");
|
||||
puts("}\n");
|
||||
#endif
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
@@ -1642,20 +1717,16 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) {
|
||||
puts("\nvoid "+modClassName(modp)+"::_eval_initial_loop("+EmitCBaseVisitor::symClassVar()+") {\n");
|
||||
puts("vlSymsp->__Vm_didInit = true;\n");
|
||||
puts("_eval_initial(vlSymsp);\n");
|
||||
#ifndef NEW_ORDERING
|
||||
puts( "vlSymsp->__Vm_activity = true;\n");
|
||||
puts( "int __VclockLoop = 0;\n");
|
||||
puts( "IData __Vchange=1;\n");
|
||||
puts( "QData __Vchange=1;\n");
|
||||
puts( "while (VL_LIKELY(__Vchange)) {\n");
|
||||
#endif
|
||||
puts( "_eval_settle(vlSymsp);\n");
|
||||
puts( "_eval(vlSymsp);\n");
|
||||
#ifndef NEW_ORDERING
|
||||
puts( "__Vchange = _change_request(vlSymsp);\n");
|
||||
puts( "if (++__VclockLoop > "+cvtToStr(v3Global.opt.convergeLimit())
|
||||
+") vl_fatal(__FILE__,__LINE__,__FILE__,\"Verilated model didn't DC converge\");\n");
|
||||
puts( "}\n");
|
||||
#endif
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
}
|
||||
@@ -1682,6 +1753,7 @@ void EmitCStmts::emitVarList(AstNode* firstp, EisWhich which, const string& pref
|
||||
case EVL_IO: doit = varp->isIO(); break;
|
||||
case EVL_SIG: doit = (varp->isSignal() && !varp->isIO()); break;
|
||||
case EVL_TEMP: doit = (varp->isTemp() && !varp->isIO()); break;
|
||||
case EVL_PAR: doit = (varp->isParam() && !varp->valuep()->castConst()); break;
|
||||
default: v3fatalSrc("Bad Case");
|
||||
}
|
||||
if (varp->isStatic() ? !isstatic : isstatic) doit=false;
|
||||
@@ -1805,6 +1877,8 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
||||
}
|
||||
}
|
||||
|
||||
emitTypedefs(modp->stmtsp());
|
||||
|
||||
puts("\n// PORTS\n");
|
||||
if (modp->isTop()) puts("// The application code writes and reads these signals to\n");
|
||||
if (modp->isTop()) puts("// propagate new values into/out from the Verilated model.\n");
|
||||
@@ -1837,6 +1911,7 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
||||
puts("\n// PARAMETERS\n");
|
||||
if (modp->isTop()) puts("// Parameters marked /*verilator public*/ for use by application code\n");
|
||||
ofp()->putsPrivate(false); // public:
|
||||
emitVarList(modp->stmtsp(), EVL_PAR, ""); // Only those that are non-CONST
|
||||
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (AstVar* varp = nodep->castVar()) {
|
||||
if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) {
|
||||
@@ -1846,7 +1921,7 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
||||
if (varp->isWide()) { // Unsupported for output
|
||||
puts("// enum WData "+varp->name()+" //wide");
|
||||
} else if (!varp->valuep()->castConst()) { // Unsupported for output
|
||||
puts("// enum IData "+varp->name()+" //not simple value");
|
||||
//puts("// enum ..... "+varp->name()+" //not simple value, see variable above instead");
|
||||
} else {
|
||||
puts("enum ");
|
||||
puts(varp->isQuad()?"_QData":"_IData");
|
||||
@@ -2061,6 +2136,7 @@ void EmitCImp::main(AstNodeModule* modp, bool slow, bool fast) {
|
||||
m_ofp = newOutCFile (modp, !m_fast, true/*source*/, splitFilenumInc());
|
||||
emitImp (modp);
|
||||
}
|
||||
splitSizeInc(10); // Even blank functions get a file with a low csplit
|
||||
mainDoFunc(funcp);
|
||||
}
|
||||
}
|
||||
@@ -2184,7 +2260,7 @@ class EmitCTrace : EmitCStmts {
|
||||
}
|
||||
|
||||
void emitTraceInitOne(AstTraceDecl* nodep) {
|
||||
if (nodep->isDouble()) {
|
||||
if (nodep->dtypep()->basicp()->isDouble()) {
|
||||
puts("vcdp->declDouble");
|
||||
} else if (nodep->isWide()) {
|
||||
puts("vcdp->declArray");
|
||||
@@ -2204,7 +2280,7 @@ class EmitCTrace : EmitCStmts {
|
||||
} else {
|
||||
puts(",-1");
|
||||
}
|
||||
if (!nodep->isDouble() // When float/double no longer have widths this can go
|
||||
if (!nodep->dtypep()->basicp()->isDouble() // When float/double no longer have widths this can go
|
||||
&& nodep->bitRange().ranged()) {
|
||||
puts(","+cvtToStr(nodep->bitRange().left())+","+cvtToStr(nodep->bitRange().right()));
|
||||
}
|
||||
@@ -2216,7 +2292,7 @@ class EmitCTrace : EmitCStmts {
|
||||
string full = ((m_funcp->funcType() == AstCFuncType::TRACE_FULL
|
||||
|| m_funcp->funcType() == AstCFuncType::TRACE_FULL_SUB)
|
||||
? "full":"chg");
|
||||
if (nodep->isDouble()) {
|
||||
if (nodep->dtypep()->basicp()->isDouble()) {
|
||||
puts("vcdp->"+full+"Double");
|
||||
} else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) {
|
||||
puts("vcdp->"+full+"Array");
|
||||
@@ -2231,7 +2307,7 @@ class EmitCTrace : EmitCStmts {
|
||||
+ ((arrayindex<0) ? 0 : (arrayindex*nodep->declp()->widthWords()))));
|
||||
puts(",");
|
||||
emitTraceValue(nodep, arrayindex);
|
||||
if (!nodep->isDouble() // When float/double no longer have widths this can go
|
||||
if (!nodep->dtypep()->basicp()->isDouble() // When float/double no longer have widths this can go
|
||||
&& (nodep->declp()->bitRange().ranged() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep))) {
|
||||
puts(","+cvtToStr(nodep->declp()->widthMin()));
|
||||
}
|
||||
@@ -2264,6 +2340,7 @@ class EmitCTrace : EmitCStmts {
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
using EmitCStmts::visit; // Suppress hidden overloaded virtual function warnng
|
||||
virtual void visit(AstNetlist* nodep, AstNUser*) {
|
||||
// Top module only
|
||||
nodep->topModulep()->accept(*this);
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ public:
|
||||
args += portp->dpiArgType(true,false);
|
||||
else if (nodep->funcPublic())
|
||||
args += portp->cPubArgType(true,false);
|
||||
else args += portp->vlArgType(true,false);
|
||||
else args += portp->vlArgType(true,false,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -203,6 +203,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
nameCheck(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
if (nodep->isSigUserRdPublic()
|
||||
&& !nodep->isParam()) { // The VPI functions require a pointer to allow modification, but parameters are constants
|
||||
@@ -220,6 +221,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep, AstNUser*) {
|
||||
nameCheck(nodep);
|
||||
if (nodep->dpiImport() || nodep->dpiExportWrapper()) {
|
||||
m_dpis.push_back(nodep);
|
||||
}
|
||||
@@ -381,7 +383,8 @@ void EmitCSyms::emitSymImp() {
|
||||
AstScope* scopep = it->first; AstNodeModule* modp = it->second;
|
||||
if (modp->isTop()) {
|
||||
} else {
|
||||
ofp()->printf("\t%c %-30s ", comma, scopep->nameDotless().c_str());
|
||||
string nameDl = scopep->nameDotless();
|
||||
ofp()->printf("\t%c %-30s ", comma, nameDl.c_str());
|
||||
puts("(Verilated::catName(topp->name(),");
|
||||
// The "." is added by catName
|
||||
putsQuoted(scopep->prettyName());
|
||||
|
||||
+7
-8
@@ -164,16 +164,16 @@ public:
|
||||
|
||||
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
|
||||
of.puts("VM_USER_CFLAGS = \\\n");
|
||||
for (V3StringSet::const_iterator it = v3Global.opt.cFlags().begin();
|
||||
it != v3Global.opt.cFlags().end(); ++it) {
|
||||
const V3StringSet& cFlags = v3Global.opt.cFlags();
|
||||
for (V3StringSet::const_iterator it = cFlags.begin(); it != cFlags.end(); ++it) {
|
||||
of.puts("\t"+*it+" \\\n");
|
||||
}
|
||||
of.puts("\n");
|
||||
|
||||
of.puts("# User LDLIBS (from -LDFLAGS on Verilator command line)\n");
|
||||
of.puts("VM_USER_LDLIBS = \\\n");
|
||||
for (V3StringSet::const_iterator it = v3Global.opt.ldLibs().begin();
|
||||
it != v3Global.opt.ldLibs().end(); ++it) {
|
||||
const V3StringSet& ldLibs = v3Global.opt.ldLibs();
|
||||
for (V3StringSet::const_iterator it = ldLibs.begin(); it != ldLibs.end(); ++it) {
|
||||
of.puts("\t"+*it+" \\\n");
|
||||
}
|
||||
of.puts("\n");
|
||||
@@ -181,8 +181,8 @@ public:
|
||||
V3StringSet dirs;
|
||||
of.puts("# User .cpp files (from .cpp's on Verilator command line)\n");
|
||||
of.puts("VM_USER_CLASSES = \\\n");
|
||||
for (V3StringSet::const_iterator it = v3Global.opt.cppFiles().begin();
|
||||
it != v3Global.opt.cppFiles().end(); ++it) {
|
||||
const V3StringSet& cppFiles = v3Global.opt.cppFiles();
|
||||
for (V3StringSet::const_iterator it = cppFiles.begin(); it != cppFiles.end(); ++it) {
|
||||
string cppfile = *it;
|
||||
of.puts("\t"+V3Options::filenameNonExt(cppfile)+" \\\n");
|
||||
string dir = V3Options::filenameDir(cppfile);
|
||||
@@ -207,8 +207,7 @@ public:
|
||||
of.puts("\n### Executable rules... (from --exe)\n");
|
||||
of.puts("VPATH += $(VM_USER_DIR)\n");
|
||||
of.puts("\n");
|
||||
for (V3StringSet::const_iterator it = v3Global.opt.cppFiles().begin();
|
||||
it != v3Global.opt.cppFiles().end(); ++it) {
|
||||
for (V3StringSet::const_iterator it = cppFiles.begin(); it != cppFiles.end(); ++it) {
|
||||
string cppfile = *it;
|
||||
string basename = V3Options::filenameNonExt(cppfile);
|
||||
of.puts(basename+".o: "+cppfile+"\n");
|
||||
|
||||
+21
-14
@@ -245,23 +245,23 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||
virtual void visit(AstFOpen* nodep, AstNUser*) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
if (nodep->filep()) nodep->filep()->iterateChildren(*this);
|
||||
if (nodep->filep()) nodep->filep()->iterateAndNext(*this);
|
||||
putbs(",");
|
||||
if (nodep->filenamep()) nodep->filenamep()->iterateChildren(*this);
|
||||
if (nodep->filenamep()) nodep->filenamep()->iterateAndNext(*this);
|
||||
putbs(",");
|
||||
if (nodep->modep()) nodep->modep()->iterateChildren(*this);
|
||||
if (nodep->modep()) nodep->modep()->iterateAndNext(*this);
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstFClose* nodep, AstNUser*) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
if (nodep->filep()) nodep->filep()->iterateChildren(*this);
|
||||
if (nodep->filep()) nodep->filep()->iterateAndNext(*this);
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstFFlush* nodep, AstNUser*) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
if (nodep->filep()) nodep->filep()->iterateChildren(*this);
|
||||
if (nodep->filep()) nodep->filep()->iterateAndNext(*this);
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstJumpGo* nodep, AstNUser*) {
|
||||
@@ -269,23 +269,23 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||
}
|
||||
virtual void visit(AstJumpLabel* nodep, AstNUser*) {
|
||||
putbs("begin : "+cvtToStr((void*)(nodep))+"\n");
|
||||
if (nodep->stmtsp()) nodep->stmtsp()->iterateChildren(*this);
|
||||
if (nodep->stmtsp()) nodep->stmtsp()->iterateAndNext(*this);
|
||||
puts("end\n");
|
||||
}
|
||||
virtual void visit(AstReadMem* nodep, AstNUser*) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
if (nodep->filenamep()) nodep->filenamep()->iterateChildren(*this);
|
||||
if (nodep->filenamep()) nodep->filenamep()->iterateAndNext(*this);
|
||||
putbs(",");
|
||||
if (nodep->memp()) nodep->memp()->iterateChildren(*this);
|
||||
if (nodep->lsbp()) { putbs(","); nodep->lsbp()->iterateChildren(*this); }
|
||||
if (nodep->msbp()) { putbs(","); nodep->msbp()->iterateChildren(*this); }
|
||||
if (nodep->memp()) nodep->memp()->iterateAndNext(*this);
|
||||
if (nodep->lsbp()) { putbs(","); nodep->lsbp()->iterateAndNext(*this); }
|
||||
if (nodep->msbp()) { putbs(","); nodep->msbp()->iterateAndNext(*this); }
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstSysIgnore* nodep, AstNUser*) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
putbs(" (");
|
||||
nodep->exprsp()->iterateChildren(*this);
|
||||
nodep->exprsp()->iterateAndNext(*this);
|
||||
puts(");\n");
|
||||
}
|
||||
virtual void visit(AstNodeFor* nodep, AstNUser*) {
|
||||
@@ -446,6 +446,14 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||
}
|
||||
puts(")");
|
||||
}
|
||||
virtual void visit(AstInitArray* nodep, AstNUser*) {
|
||||
putfs(nodep,"`{");
|
||||
for (AstNode* subp = nodep->initsp(); subp; subp=subp->nextp()) {
|
||||
subp->accept(*this);
|
||||
if (subp->nextp()) putbs(",");
|
||||
}
|
||||
puts("}");
|
||||
}
|
||||
virtual void visit(AstNodeCond* nodep, AstNUser*) {
|
||||
putbs("(");
|
||||
nodep->condp()->iterateAndNext(*this); putfs(nodep," ? ");
|
||||
@@ -560,7 +568,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
putfs(nodep,nodep->verilogKwd());
|
||||
puts(" ");
|
||||
nodep->dtypep()->iterateAndNext(*this); puts(" ");
|
||||
nodep->dtypep()->iterate(*this); puts(" ");
|
||||
puts(nodep->prettyName());
|
||||
puts(";\n");
|
||||
}
|
||||
@@ -677,7 +685,6 @@ public:
|
||||
class EmitVPrefixedVisitor : public EmitVBaseVisitor {
|
||||
// MEMBERS
|
||||
EmitVPrefixedFormatter m_formatter; // Special verilog formatter (Way down the inheritance is another unused V3OutFormatter)
|
||||
bool m_user3mark; // nodep->user3() if set means mark with %%
|
||||
// METHODS
|
||||
virtual void putsNoTracking(const string& str) { m_formatter.putsNoTracking(str); }
|
||||
virtual void puts(const string& str) { m_formatter.puts(str); }
|
||||
@@ -698,7 +705,7 @@ class EmitVPrefixedVisitor : public EmitVBaseVisitor {
|
||||
public:
|
||||
EmitVPrefixedVisitor(AstNode* nodep, ostream& os, const string& prefix, int flWidth,
|
||||
AstSenTree* domainp, bool user3mark)
|
||||
: EmitVBaseVisitor(domainp), m_formatter(os, prefix, flWidth), m_user3mark(user3mark) {
|
||||
: EmitVBaseVisitor(domainp), m_formatter(os, prefix, flWidth) {
|
||||
if (user3mark) { AstUser3InUse::check(); }
|
||||
nodep->accept(*this);
|
||||
}
|
||||
|
||||
@@ -253,6 +253,7 @@ void FileLine::warnStyleOff(bool flag) {
|
||||
|
||||
bool FileLine::warnIsOff(V3ErrorCode code) const {
|
||||
if (!m_warnOn.test(code)) return true;
|
||||
if (!defaultFileLine().m_warnOn.test(code)) return true; // Global overrides local
|
||||
// UNOPTFLAT implies UNOPT
|
||||
if (code==V3ErrorCode::UNOPT && !m_warnOn.test(V3ErrorCode::UNOPTFLAT)) return true;
|
||||
if ((code.lintError() || code.styleError()) && !m_warnOn.test(V3ErrorCode::I_LINT)) return true;
|
||||
|
||||
+13
-4
@@ -84,6 +84,7 @@ public:
|
||||
MULTIDRIVEN, // Driven from multiple blocks
|
||||
PINMISSING, // Cell pin not specified
|
||||
PINNOCONNECT, // Cell pin not connected
|
||||
PINCONNECTEMPTY,// Cell pin connected by name with empty reference: ".name()" (can be used to mark unused pins)
|
||||
REALCVT, // Real conversion
|
||||
REDEFMACRO, // Redefining existing define macro
|
||||
SELRANGE, // Selection index out of range
|
||||
@@ -127,7 +128,7 @@ public:
|
||||
"INCABSPATH", "INITIALDLY",
|
||||
"LITENDIAN", "MODDUP",
|
||||
"MULTIDRIVEN",
|
||||
"PINMISSING", "PINNOCONNECT",
|
||||
"PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY",
|
||||
"REALCVT", "REDEFMACRO",
|
||||
"SELRANGE", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
|
||||
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNPACKED", "UNSIGNED", "UNUSED",
|
||||
@@ -143,9 +144,10 @@ public:
|
||||
// Warnings we'll present to the user as errors
|
||||
// Later -Werror- options may make more of these.
|
||||
bool pretendError() const { return ( m_e==ASSIGNIN || m_e==BLKANDNBLK
|
||||
|| m_e==IMPURE || m_e==MODDUP || m_e==SYMRSVDWORD); }
|
||||
|| m_e==IMPURE || m_e==MODDUP); }
|
||||
// Warnings to mention manual
|
||||
bool mentionManual() const { return ( m_e==EC_FATALSRC || pretendError() ); }
|
||||
bool mentionManual() const { return ( m_e==EC_FATALSRC || m_e==SYMRSVDWORD
|
||||
|| pretendError() ); }
|
||||
|
||||
// Warnings that are lint only
|
||||
bool lintError() const { return ( m_e==ALWCOMBORDER
|
||||
@@ -165,6 +167,7 @@ public:
|
||||
|| m_e==DEFPARAM
|
||||
|| m_e==DECLFILENAME
|
||||
|| m_e==INCABSPATH
|
||||
|| m_e==PINCONNECTEMPTY
|
||||
|| m_e==PINNOCONNECT
|
||||
|| m_e==SYNCASYNCNET
|
||||
|| m_e==UNDRIVEN
|
||||
@@ -270,12 +273,18 @@ template< class T> std::string cvtToStr (const T& t) {
|
||||
|
||||
inline uint32_t cvtToHash(const void* vp) {
|
||||
// We can shove a 64 bit pointer into a 32 bit bucket
|
||||
// On 32 bit systems, lower is always 0, but who cares?
|
||||
// On 32-bit systems, lower is always 0, but who cares?
|
||||
union { const void* up; struct {uint32_t upper; uint32_t lower;} l;} u;
|
||||
u.l.upper=0; u.l.lower=0; u.up=vp;
|
||||
return u.l.upper^u.l.lower;
|
||||
}
|
||||
|
||||
inline string ucfirst(const string& text) {
|
||||
string out = text;
|
||||
out[0] = toupper(out[0]);
|
||||
return out;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
class FileLine;
|
||||
|
||||
+7
-3
@@ -448,8 +448,8 @@ private:
|
||||
AstNode* newp = new AstShiftR (nodep->fileline(),
|
||||
fromp,
|
||||
dropCondBound(lsbp),
|
||||
nodep->width());
|
||||
newp->dtypeFrom(nodep);
|
||||
fromp->width()); // {large}>>32 requires 64-bit shift operation; then cast
|
||||
newp->dtypeFrom(fromp);
|
||||
if (!nodep->isQuad() && fromp->isQuad()) {
|
||||
newp = new AstCCast (newp->fileline(), newp, nodep);
|
||||
}
|
||||
@@ -583,11 +583,15 @@ private:
|
||||
newSelBitBit(lhsp->lsbp()),
|
||||
VL_WORDSIZE)),
|
||||
oldvalp);
|
||||
|
||||
// Restrict the shift amount to 0-31, see bug804.
|
||||
AstNode* shiftp = new AstAnd(nodep->fileline(), lhsp->lsbp()->cloneTree(true),
|
||||
new AstConst(nodep->fileline(), VL_WORDSIZE-1));
|
||||
AstNode* newp = new AstOr (lhsp->fileline(),
|
||||
oldvalp,
|
||||
new AstShiftL (lhsp->fileline(),
|
||||
rhsp,
|
||||
lhsp->lsbp()->cloneTree(true),
|
||||
shiftp,
|
||||
VL_WORDSIZE));
|
||||
newp = new AstAssign (nodep->fileline(),
|
||||
new AstWordSel (nodep->fileline(),
|
||||
|
||||
+21
-11
@@ -30,7 +30,7 @@
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
#if defined(__unix__)
|
||||
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
|
||||
# define INFILTER_PIPE // Allow pipe filtering. Needs fork()
|
||||
#endif
|
||||
|
||||
@@ -67,10 +67,12 @@ class V3FileDependImp {
|
||||
const string& filename() const { return m_filename; }
|
||||
bool target() const { return m_target; }
|
||||
off_t size() const { return m_stat.st_size; }
|
||||
ino_t ino() const { return m_stat.st_ino; }
|
||||
time_t mtime() const { return m_stat.st_mtime; }
|
||||
void loadStats() {
|
||||
if (!m_stat.st_mtime) {
|
||||
int err = stat(filename().c_str(), &m_stat);
|
||||
string fn = filename();
|
||||
int err = stat(fn.c_str(), &m_stat);
|
||||
if (err!=0) {
|
||||
m_stat.st_mtime = 1;
|
||||
// Not a error... This can occur due to `line directives in the .vpp files
|
||||
@@ -119,7 +121,7 @@ V3FileDependImp dependImp; // Depend implementation class
|
||||
// V3FileDependImp
|
||||
|
||||
inline void V3FileDependImp::writeDepend(const string& filename) {
|
||||
const auto_ptr<ofstream> ofp (V3File::new_ofstream(filename));
|
||||
const VL_UNIQUE_PTR<ofstream> ofp (V3File::new_ofstream(filename));
|
||||
if (ofp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
|
||||
for (set<DependFile>::iterator iter=m_filenameList.begin();
|
||||
@@ -155,7 +157,7 @@ inline void V3FileDependImp::writeDepend(const string& filename) {
|
||||
}
|
||||
|
||||
inline void V3FileDependImp::writeTimes(const string& filename, const string& cmdlineIn) {
|
||||
const auto_ptr<ofstream> ofp (V3File::new_ofstream(filename));
|
||||
const VL_UNIQUE_PTR<ofstream> ofp (V3File::new_ofstream(filename));
|
||||
if (ofp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
|
||||
string cmdline = stripQuotes(cmdlineIn);
|
||||
@@ -169,10 +171,12 @@ inline void V3FileDependImp::writeTimes(const string& filename, const string& cm
|
||||
V3Options::fileNfsFlush(dfp->filename());
|
||||
dfp->loadStats();
|
||||
off_t showSize = iter->size();
|
||||
if (dfp->filename() == filename) showSize=0; // We're writing it, so need to ignore it
|
||||
ino_t showIno = iter->ino();
|
||||
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->mtime();
|
||||
*ofp<<" \""<<iter->filename()<<"\"";
|
||||
*ofp<<endl;
|
||||
@@ -202,6 +206,7 @@ inline bool V3FileDependImp::checkTimes(const string& filename, const string& cm
|
||||
while (!ifp->eof()) {
|
||||
char chkDir; *ifp>>chkDir;
|
||||
off_t chkSize; *ifp>>chkSize;
|
||||
ino_t chkIno; *ifp>>chkIno;
|
||||
if (ifp->eof()) break; // Needed to read final whitespace before found eof
|
||||
time_t chkMtime; *ifp>>chkMtime;
|
||||
char quote; *ifp>>quote;
|
||||
@@ -222,6 +227,7 @@ inline bool V3FileDependImp::checkTimes(const string& filename, const string& cm
|
||||
// we determined the original size. For safety, we know the creation time
|
||||
// must be within a few second window... call it 20 sec.
|
||||
if (!(chkStat.st_size >= chkSize
|
||||
&& chkStat.st_ino == chkIno
|
||||
&& chkStat.st_mtime >= chkMtime
|
||||
&& chkStat.st_mtime <= (chkMtime + 20))) {
|
||||
UINFO(2," --check-times failed: out-of-date "<<chkFilename
|
||||
@@ -253,15 +259,19 @@ bool V3File::checkTimes(const string& filename, const string& cmdline) {
|
||||
return dependImp.checkTimes(filename, cmdline);
|
||||
}
|
||||
|
||||
void V3File::createDir(const string& dirname) {
|
||||
#ifndef _WIN32
|
||||
mkdir(dirname.c_str(), 0777);
|
||||
#else
|
||||
mkdir(dirname.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
void V3File::createMakeDir() {
|
||||
static bool created = false;
|
||||
if (!created) {
|
||||
created = true;
|
||||
#ifndef _WIN32
|
||||
mkdir(v3Global.opt.makeDir().c_str(), 0777);
|
||||
#else
|
||||
mkdir(v3Global.opt.makeDir().c_str());
|
||||
#endif
|
||||
createDir(v3Global.opt.makeDir());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,7 +567,7 @@ const char* V3OutFormatter::indentStr(int num) {
|
||||
static char str[MAXSPACE+20];
|
||||
char* cp = str;
|
||||
if (num>MAXSPACE) num=MAXSPACE;
|
||||
if (!m_lang==LA_VERILOG) { // verilogPrefixedTree doesn't want tabs
|
||||
if (m_lang!=LA_VERILOG) { // verilogPrefixedTree doesn't want tabs
|
||||
while (num>=8) {
|
||||
*cp++ = '\t';
|
||||
num -= 8;
|
||||
|
||||
+3
-2
@@ -67,6 +67,7 @@ public:
|
||||
static bool checkTimes(const string& filename, const string& cmdline);
|
||||
|
||||
// Directory utilities
|
||||
static void createDir(const string& dirname);
|
||||
static void createMakeDir();
|
||||
};
|
||||
|
||||
@@ -188,8 +189,8 @@ public:
|
||||
}
|
||||
virtual ~V3OutCFile() {}
|
||||
virtual void putsCellDecl(const string& classname, const string& cellname) {
|
||||
this->printf("%-19s\t%s;\n",
|
||||
(classname + "*").c_str(),cellname.c_str());
|
||||
string classStar = classname + "*";
|
||||
this->printf("%-19s\t%s;\n", classStar.c_str(), cellname.c_str());
|
||||
}
|
||||
virtual void putsHeader() { puts("// Verilated -*- C++ -*-\n"); }
|
||||
virtual void putsIntTopInclude() { }
|
||||
|
||||
+143
-8
@@ -222,7 +222,7 @@ private:
|
||||
// V3Const cleans up any NOTs by flipping the edges for us
|
||||
if (m_buffersOnly
|
||||
&& !(nodep->rhsp()->castVarRef()
|
||||
// Until NEW_ORDERING, avoid making non-clocked logic into clocked,
|
||||
// Avoid making non-clocked logic into clocked,
|
||||
// as it slows down the verilator_sim_benchmark
|
||||
|| (nodep->rhsp()->castNot()
|
||||
&& nodep->rhsp()->castNot()->lhsp()->castVarRef()
|
||||
@@ -258,8 +258,8 @@ public:
|
||||
if (!m_substTreep) {
|
||||
clearSimple("No assignment found\n");
|
||||
}
|
||||
for (GateVarRefList::const_iterator it = rhsVarRefs().begin();
|
||||
it != rhsVarRefs().end(); ++it) {
|
||||
for (GateVarRefList::const_iterator it = m_rhsVarRefs.begin();
|
||||
it != m_rhsVarRefs.end(); ++it) {
|
||||
if (m_lhsVarRef && m_lhsVarRef->varScopep() == (*it)->varScopep()) {
|
||||
clearSimple("Circular logic\n"); // Oh my, we'll get a UNOPTFLAT much later.
|
||||
}
|
||||
@@ -303,6 +303,7 @@ private:
|
||||
V3Double0 m_statSigs; // Statistic tracking
|
||||
V3Double0 m_statRefs; // Statistic tracking
|
||||
V3Double0 m_statDedupLogic; // Statistic tracking
|
||||
V3Double0 m_statAssignMerged; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
void iterateNewStmt(AstNode* nodep, const char* nonReducibleReason, const char* consumeReason) {
|
||||
@@ -352,6 +353,7 @@ private:
|
||||
void consumedMove();
|
||||
void replaceAssigns();
|
||||
void dedupe();
|
||||
void mergeAssigns();
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep, AstNUser*) {
|
||||
@@ -368,6 +370,7 @@ private:
|
||||
optimizeSignals(true);
|
||||
// Remove redundant logic
|
||||
if (v3Global.opt.oDedupe()) dedupe();
|
||||
if (v3Global.opt.oAssemble()) mergeAssigns();
|
||||
// Warn
|
||||
warnSignals();
|
||||
consumedMark();
|
||||
@@ -506,6 +509,7 @@ public:
|
||||
V3Stats::addStat("Optimizations, Gate sigs deleted", m_statSigs);
|
||||
V3Stats::addStat("Optimizations, Gate inputs replaced", m_statRefs);
|
||||
V3Stats::addStat("Optimizations, Gate sigs deduped", m_statDedupLogic);
|
||||
V3Stats::addStat("Optimizations, Gate assign merged", m_statAssignMerged);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -593,8 +597,9 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
||||
optimizeElimVar(vvertexp->varScp(), substp, consumerp);
|
||||
// If the new replacement referred to a signal,
|
||||
// Correct the graph to point to this new generating variable
|
||||
for (GateVarRefList::const_iterator it = okVisitor.rhsVarRefs().begin();
|
||||
it != okVisitor.rhsVarRefs().end(); ++it) {
|
||||
const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs();
|
||||
for (GateVarRefList::const_iterator it = rhsVarRefs.begin();
|
||||
it != rhsVarRefs.end(); ++it) {
|
||||
AstVarScope* newvarscp = (*it)->varScopep();
|
||||
UINFO(9," Point-to-new vertex "<<newvarscp<<endl);
|
||||
GateVarVertex* varvertexp = makeVarVertex(newvarscp);
|
||||
@@ -929,7 +934,12 @@ private:
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
GateDedupeVarVisitor() {}
|
||||
GateDedupeVarVisitor() {
|
||||
m_assignp = NULL;
|
||||
m_ifCondp = NULL;
|
||||
m_always = false;
|
||||
m_dedupable = true;
|
||||
}
|
||||
// PUBLIC METHODS
|
||||
AstNodeVarRef* findDupe(AstNode* nodep, AstVarScope* consumerVarScopep, AstActive* activep) {
|
||||
m_assignp = NULL;
|
||||
@@ -978,7 +988,7 @@ private:
|
||||
AstVarScope* dupVarScopep = dupVarRefp->varScopep();
|
||||
GateVarVertex* dupVvertexp = (GateVarVertex*) (dupVarScopep->user1p());
|
||||
UINFO(4,"replacing " << vvertexp << " with " << dupVvertexp << endl);
|
||||
m_numDeduped++;
|
||||
++m_numDeduped;
|
||||
// Replace all of this varvertex's consumers with dupVarRefp
|
||||
for (V3GraphEdge* outedgep = vvertexp->outBeginp();outedgep;) {
|
||||
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(outedgep->top());
|
||||
@@ -1003,7 +1013,6 @@ private:
|
||||
lvertexp->user(true);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1057,6 +1066,132 @@ void GateVisitor::dedupe() {
|
||||
m_statDedupLogic += deduper.numDeduped();
|
||||
}
|
||||
|
||||
|
||||
//######################################################################
|
||||
// Recurse through the graph, try to merge assigns
|
||||
|
||||
class GateMergeAssignsGraphVisitor : public GateGraphBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
AstNodeAssign* m_assignp;
|
||||
AstActive* m_activep;
|
||||
GateLogicVertex* m_logicvp;
|
||||
V3Graph* m_graphp;
|
||||
V3Double0 m_numMergedAssigns; // Statistic tracking
|
||||
|
||||
|
||||
// assemble two Sel into one if possible
|
||||
AstSel* merge(AstSel* pre, AstSel* cur) {
|
||||
AstVarRef* preVarRefp = pre->fromp()->castVarRef();
|
||||
AstVarRef* curVarRefp = cur->fromp()->castVarRef();
|
||||
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();
|
||||
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());
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
virtual AstNUser* visit(GateVarVertex *vvertexp, AstNUser*) {
|
||||
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; ) {
|
||||
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);
|
||||
// first assign with Sel-lhs
|
||||
if (!m_activep) m_activep = lvertexp->activep();
|
||||
if (!m_logicvp) m_logicvp = lvertexp;
|
||||
if (!m_assignp) m_assignp = assignp;
|
||||
|
||||
// not under the same active
|
||||
if (m_activep != lvertexp->activep()) {
|
||||
m_activep = lvertexp->activep();
|
||||
m_logicvp = lvertexp;
|
||||
m_assignp = assignp;
|
||||
continue;
|
||||
}
|
||||
|
||||
AstSel* preselp = m_assignp->lhsp()->castSel();
|
||||
AstSel* curselp = assignp->lhsp()->castSel();
|
||||
if (!preselp || !curselp) continue;
|
||||
|
||||
if (AstSel* newselp = merge(preselp, curselp)) {
|
||||
UINFO(5, "assemble to new sel: "<<newselp<<endl);
|
||||
// replace preSel with newSel
|
||||
preselp->replaceWith(newselp); preselp->deleteTree(); preselp = NULL;
|
||||
// create new rhs for pre assignment
|
||||
AstNode* newrhsp = new AstConcat(m_assignp->rhsp()->fileline(), m_assignp->rhsp()->cloneTree(false), assignp->rhsp()->cloneTree(false));
|
||||
AstNode* oldrhsp = m_assignp->rhsp();
|
||||
oldrhsp->replaceWith(newrhsp); oldrhsp->deleteTree(); oldrhsp = NULL;
|
||||
m_assignp->dtypeChgWidthSigned(m_assignp->width()+assignp->width(), m_assignp->width()+assignp->width(), AstNumeric::fromBool(true));
|
||||
// don't need to delete, will be handled
|
||||
//assignp->unlinkFrBack(); assignp->deleteTree(); assignp = NULL;
|
||||
|
||||
// update the graph
|
||||
{
|
||||
// delete all inedges to lvertexp
|
||||
if (!lvertexp->inEmpty()) {
|
||||
for (V3GraphEdge* ledgep = lvertexp->inBeginp(); ledgep; ) {
|
||||
V3GraphEdge* oedgep = ledgep;
|
||||
ledgep = ledgep->inNextp();
|
||||
GateEitherVertex* fromvp = dynamic_cast<GateEitherVertex*>(oedgep->fromp());
|
||||
new V3GraphEdge(m_graphp, fromvp, m_logicvp, 1);
|
||||
oedgep->unlinkDelete(); oedgep = NULL;
|
||||
}
|
||||
}
|
||||
// delete all outedges to lvertexp, only one
|
||||
oldedgep->unlinkDelete(); oldedgep = NULL;
|
||||
}
|
||||
++m_numMergedAssigns;
|
||||
} else {
|
||||
m_assignp = assignp;
|
||||
m_logicvp = lvertexp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual AstNUser* visit(GateLogicVertex* lvertexp, AstNUser* vup) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
GateMergeAssignsGraphVisitor(V3Graph* graphp) {
|
||||
m_assignp = NULL;
|
||||
m_activep = NULL;
|
||||
m_logicvp = NULL;
|
||||
m_numMergedAssigns = 0;
|
||||
m_graphp = graphp;
|
||||
}
|
||||
void mergeAssignsTree(GateVarVertex* vvertexp) {
|
||||
vvertexp->accept(*this);
|
||||
}
|
||||
V3Double0 numMergedAssigns() { return m_numMergedAssigns; }
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void GateVisitor::mergeAssigns() {
|
||||
GateMergeAssignsGraphVisitor merger(&m_graph);
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
merger.mergeAssignsTree(vvertexp);
|
||||
}
|
||||
}
|
||||
m_statAssignMerged += merger.numMergedAssigns();
|
||||
}
|
||||
|
||||
|
||||
//######################################################################
|
||||
// Convert VARSCOPE(ASSIGN(default, VARREF)) to just VARSCOPE(default)
|
||||
|
||||
|
||||
+2
-2
@@ -115,7 +115,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstActive* nodep, AstNUser*) {
|
||||
m_activep = nodep;
|
||||
nodep->sensesp()->iterateChildren(*this);
|
||||
nodep->sensesp()->iterateChildren(*this); // iterateAndNext?
|
||||
m_activep = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
@@ -201,7 +201,7 @@ private:
|
||||
virtual void visit(AstActive* nodep, AstNUser*) {
|
||||
UINFO(8,"ACTIVE "<<nodep<<endl);
|
||||
m_activep = nodep;
|
||||
nodep->sensesp()->iterateChildren(*this);
|
||||
nodep->sensesp()->iterateChildren(*this); // iterateAndNext?
|
||||
m_activep = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ class V3Global {
|
||||
int m_debugFileNumber; // Number to append to debug files created
|
||||
bool m_assertDTypesResolved; // Tree should have dtypep()'s
|
||||
bool m_assertWidthsMatch; // Tree should have width()==widthMin()
|
||||
bool m_constRemoveXs; // Const needs to strip any Xs
|
||||
bool m_needHInlines; // Need __Inlines file
|
||||
bool m_needHeavy; // Need verilated_heavy.h include
|
||||
bool m_dpi; // Need __Dpi include files
|
||||
@@ -58,6 +59,7 @@ public:
|
||||
m_debugFileNumber = 0;
|
||||
m_assertDTypesResolved = false;
|
||||
m_assertWidthsMatch = false;
|
||||
m_constRemoveXs = false;
|
||||
m_needHInlines = false;
|
||||
m_needHeavy = false;
|
||||
m_dpi = false;
|
||||
@@ -77,6 +79,8 @@ public:
|
||||
static void dumpCheckGlobalTree(const string& filename, int newNumber=0, bool doDump=true);
|
||||
void assertDTypesResolved(bool flag) { m_assertDTypesResolved = flag; }
|
||||
void assertWidthsMatch(bool flag) { m_assertWidthsMatch = flag; }
|
||||
bool constRemoveXs() const { return m_constRemoveXs; }
|
||||
void constRemoveXs(bool flag) { m_constRemoveXs = flag; }
|
||||
string debugFilename(const string& nameComment, int newNumber=0) {
|
||||
++m_debugFileNumber;
|
||||
if (newNumber) m_debugFileNumber = newNumber;
|
||||
|
||||
@@ -96,6 +96,7 @@ public:
|
||||
DfaVertex(DfaGraph* graphp, bool start=false, bool accepting=false)
|
||||
: V3GraphVertex(graphp)
|
||||
, m_start(start), m_accepting(accepting) {}
|
||||
using V3GraphVertex::clone; // We are overriding, not overloading clone(V3Graph*)
|
||||
virtual DfaVertex* clone(DfaGraph* graphp) {
|
||||
return new DfaVertex(graphp, start(), accepting()); }
|
||||
virtual ~DfaVertex() {}
|
||||
|
||||
+7
-2
@@ -80,7 +80,7 @@ private:
|
||||
if (m_modp->user2() != CIL_NOTHARD) {
|
||||
UINFO(4," No inline hard: "<<reason<<" "<<m_modp<<endl);
|
||||
m_modp->user2(CIL_NOTHARD);
|
||||
m_statUnsup++;
|
||||
++m_statUnsup;
|
||||
}
|
||||
} else {
|
||||
if (m_modp->user2() == CIL_MAYBE) {
|
||||
@@ -314,6 +314,7 @@ private:
|
||||
// Also clear I/O bits, as it is now local.
|
||||
string name = m_cellp->name() + "__DOT__" + nodep->name();
|
||||
if (!nodep->isFuncLocal()) nodep->inlineAttrReset(name);
|
||||
if (!m_cellp->isTrace()) nodep->trace(false);
|
||||
if (debug()>=9) { nodep->dumpTree(cout,"varchanged:"); }
|
||||
if (debug()>=9) { nodep->valuep()->dumpTree(cout,"varchangei:"); }
|
||||
// Iterate won't hit AstIfaceRefDType directly as it is no longer underneath the module
|
||||
@@ -387,6 +388,10 @@ private:
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_cellp->name()));
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
afterp = nodep->scopeEntrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeEntrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_cellp->name()));
|
||||
if (afterp) nodep->scopeEntrp(afterp);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstCoverDecl* nodep, AstNUser*) {
|
||||
@@ -504,7 +509,7 @@ private:
|
||||
UINFO(6," -to "<<pinNewVarp<<endl);
|
||||
pinNewVarp->user2p(connectRefp);
|
||||
// Public output inside the cell must go via an assign rather than alias
|
||||
// Else the public logic will set the alias, loosing the value to be propagated up
|
||||
// Else the public logic will set the alias, losing the value to be propagated up
|
||||
// (InOnly isn't a problem as the AssignAlias will create the assignment for us)
|
||||
pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() && pinNewVarp->isOutOnly());
|
||||
}
|
||||
|
||||
+7
-1
@@ -308,7 +308,13 @@ private:
|
||||
set<string> ports; // Symbol table of all connected port names
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
if (pinp->name()=="") pinp->v3error("Connect by position is illegal in .* connected cells");
|
||||
if (!pinp->exprp()) pinp->v3warn(PINNOCONNECT,"Cell pin is not connected: "<<pinp->prettyName());
|
||||
if (!pinp->exprp()) {
|
||||
if (pinp->name().substr(0, 11) == "__pinNumber") {
|
||||
pinp->v3warn(PINNOCONNECT,"Cell pin is not connected: "<<pinp->prettyName());
|
||||
} else {
|
||||
pinp->v3warn(PINCONNECTEMPTY,"Cell pin connected by name with empty reference: "<<pinp->prettyName());
|
||||
}
|
||||
}
|
||||
if (ports.find(pinp->name()) == ports.end()) {
|
||||
ports.insert(pinp->name());
|
||||
}
|
||||
|
||||
+49
-33
@@ -96,6 +96,12 @@ private:
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser4InUse m_inuser4;
|
||||
|
||||
public:
|
||||
// ENUMS
|
||||
// In order of priority, compute first ... compute last
|
||||
enum SAMNum { SAMN_MODPORT, SAMN_IFTOP, SAMN__MAX }; // Values for m_scopeAliasMap
|
||||
|
||||
private:
|
||||
// TYPES
|
||||
typedef multimap<string,VSymEnt*> NameScopeSymMap;
|
||||
typedef map<VSymEnt*,VSymEnt*> ScopeAliasMap;
|
||||
@@ -110,7 +116,7 @@ private:
|
||||
VSymEnt* m_dunitEntp; // $unit entry
|
||||
NameScopeSymMap m_nameScopeSymMap; // Map of scope referenced by non-pretty textual name
|
||||
ImplicitNameSet m_implicitNameSet; // For [module][signalname] if we can implicitly create it
|
||||
ScopeAliasMap m_scopeAliasMap; // Map of <lhs,rhs> aliases
|
||||
ScopeAliasMap m_scopeAliasMap[SAMN__MAX]; // Map of <lhs,rhs> aliases
|
||||
IfaceVarSyms m_ifaceVarSyms; // List of AstIfaceRefDType's to be imported
|
||||
IfaceModSyms m_ifaceModSyms; // List of AstIface+Symbols to be processed
|
||||
bool m_forPrimary; // First link
|
||||
@@ -127,13 +133,22 @@ public:
|
||||
void dump(const string& nameComment="linkdot", bool force=false) {
|
||||
if (debug()>=6 || force) {
|
||||
string filename = v3Global.debugFilename(nameComment)+".txt";
|
||||
const auto_ptr<ofstream> logp (V3File::new_ofstream(filename));
|
||||
const VL_UNIQUE_PTR<ofstream> logp (V3File::new_ofstream(filename));
|
||||
if (logp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
ostream& os = *logp;
|
||||
m_syms.dump(os);
|
||||
if (!m_scopeAliasMap.empty()) os<<"\nScopeAliasMap:\n";
|
||||
for (ScopeAliasMap::iterator it = m_scopeAliasMap.begin(); it != m_scopeAliasMap.end(); ++it) {
|
||||
os<<"\t"<<it->first<<" -> "<<it->second<<endl;
|
||||
bool first = true;
|
||||
for (int samn=0; samn<SAMN__MAX; ++samn) {
|
||||
if (!m_scopeAliasMap[samn].empty()) {
|
||||
if (first) os<<"\nScopeAliasMap:\n";
|
||||
first = false;
|
||||
for (ScopeAliasMap::iterator it = m_scopeAliasMap[samn].begin();
|
||||
it != m_scopeAliasMap[samn].end(); ++it) {
|
||||
// left side is what we will import into
|
||||
os<<"\t"<<samn<<"\t"<<it->first<<" ("<<it->first->nodep()->typeName()
|
||||
<<") <- "<<it->second<<" "<<it->second->nodep()<<endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,11 +196,6 @@ public:
|
||||
else if (nodep->castIface()) return "interface";
|
||||
else return nodep->prettyTypeName();
|
||||
}
|
||||
static string ucfirst(const string& text) {
|
||||
string out = text;
|
||||
out[0] = toupper(out[0]);
|
||||
return out;
|
||||
}
|
||||
|
||||
VSymEnt* rootEntp() const { return m_syms.rootp(); }
|
||||
VSymEnt* dunitEntp() const { return m_dunitEntp; }
|
||||
@@ -256,8 +266,8 @@ public:
|
||||
if (nodep->modp()) nodep->modp()->user1p(symp);
|
||||
checkDuplicate(abovep, nodep, nodep->origName());
|
||||
abovep->reinsert(nodep->origName(), symp);
|
||||
if (abovep != modSymp && !modSymp->findIdFlat(nodep->name())) {
|
||||
// If it's foo_DOT_bar, we need to be able to find it under that too.
|
||||
if (forScopeCreation() && abovep != modSymp && !modSymp->findIdFlat(nodep->name())) {
|
||||
// If it's foo_DOT_bar, we need to be able to find it under "foo_DOT_bar" too.
|
||||
// Duplicates are possible, as until resolve generates might have 2 same cells under an if
|
||||
modSymp->reinsert(nodep->name(), symp);
|
||||
}
|
||||
@@ -382,31 +392,36 @@ public:
|
||||
<<"': "<<ifacerefp->prettyName(ifacerefp->modportName()));
|
||||
}
|
||||
// Alias won't expand until interfaces and modport names are known; see notes at top
|
||||
insertScopeAlias(varSymp, ifOrPortSymp);
|
||||
insertScopeAlias(SAMN_IFTOP, varSymp, ifOrPortSymp);
|
||||
}
|
||||
m_ifaceVarSyms.clear();
|
||||
}
|
||||
|
||||
// Track and later insert scope aliases
|
||||
void insertScopeAlias(VSymEnt* lhsp, VSymEnt* rhsp) { // Typically lhsp=VAR w/dtype IFACEREF, rhsp=IFACE cell
|
||||
void insertScopeAlias(SAMNum samn, VSymEnt* lhsp, VSymEnt* rhsp) {
|
||||
// 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);
|
||||
m_scopeAliasMap.insert(make_pair(lhsp, rhsp));
|
||||
m_scopeAliasMap[samn].insert(make_pair(lhsp, rhsp));
|
||||
}
|
||||
void computeScopeAliases() {
|
||||
UINFO(9,"computeIfaceAliases\n");
|
||||
for (ScopeAliasMap::iterator it=m_scopeAliasMap.begin(); it!=m_scopeAliasMap.end(); ++it) {
|
||||
VSymEnt* lhsp = it->first;
|
||||
VSymEnt* srcp = lhsp;
|
||||
while (1) { // Follow chain of aliases up to highest level non-alias
|
||||
ScopeAliasMap::iterator it2 = m_scopeAliasMap.find(srcp);
|
||||
if (it2 != m_scopeAliasMap.end()) { srcp = it2->second; continue; }
|
||||
else break;
|
||||
for (int samn=0; samn<SAMN__MAX; ++samn) {
|
||||
for (ScopeAliasMap::iterator it=m_scopeAliasMap[samn].begin();
|
||||
it!=m_scopeAliasMap[samn].end(); ++it) {
|
||||
VSymEnt* lhsp = it->first;
|
||||
VSymEnt* srcp = lhsp;
|
||||
while (1) { // Follow chain of aliases up to highest level non-alias
|
||||
ScopeAliasMap::iterator it2 = m_scopeAliasMap[samn].find(srcp);
|
||||
if (it2 != m_scopeAliasMap[samn].end()) { srcp = it2->second; continue; }
|
||||
else break;
|
||||
}
|
||||
UINFO(9," iiasa: Insert alias se"<<lhsp<<" ("<<lhsp->nodep()->typeName()
|
||||
<<") <- se"<<srcp<<" "<<srcp->nodep()<<endl);
|
||||
// srcp should be an interface reference pointing to the interface we want to import
|
||||
lhsp->importFromIface(symsp(), srcp);
|
||||
}
|
||||
UINFO(9," iiasa: Insert alias se"<<lhsp<<" <- se"<<srcp<<" "<<srcp->nodep()<<endl);
|
||||
// srcp should be an interface reference pointing to the interface we want to import
|
||||
lhsp->importFromIface(symsp(), srcp);
|
||||
//m_scopeAliasMap[samn].clear(); // Done with it, but put into debug file
|
||||
}
|
||||
m_scopeAliasMap.clear();
|
||||
}
|
||||
private:
|
||||
VSymEnt* findWithAltFallback(VSymEnt* symp, const string& name, const string& altname) {
|
||||
@@ -1105,7 +1120,7 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
||||
}
|
||||
// Interface reference; need to put whole thing into symtable, but can't clone it now
|
||||
// as we may have a later alias for it.
|
||||
m_statep->insertScopeAlias(varSymp, cellSymp);
|
||||
m_statep->insertScopeAlias(LinkDotState::SAMN_IFTOP, varSymp, cellSymp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1151,7 +1166,7 @@ class LinkDotScopeVisitor : public AstNVisitor {
|
||||
}
|
||||
// Remember the alias - can't do it yet because we may have additional symbols to be added,
|
||||
// or maybe an alias of an alias
|
||||
m_statep->insertScopeAlias(lhsSymp, rhsSymp);
|
||||
m_statep->insertScopeAlias(LinkDotState::SAMN_IFTOP, lhsSymp, rhsSymp);
|
||||
// We have stored the link, we don't need these any more
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
||||
}
|
||||
@@ -1212,7 +1227,8 @@ class LinkDotIfaceVisitor : public AstNVisitor {
|
||||
} else if (AstNodeFTask* ftaskp = symp->nodep()->castNodeFTask()) {
|
||||
// Make symbol under modport that points at the _interface_'s var, not the modport.
|
||||
nodep->ftaskp(ftaskp);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), ftaskp, NULL/*package*/);
|
||||
VSymEnt* subSymp = m_statep->insertSym(m_curSymp, nodep->name(), ftaskp, NULL/*package*/);
|
||||
m_statep->insertScopeAlias(LinkDotState::SAMN_MODPORT, subSymp, symp);
|
||||
} else {
|
||||
nodep->v3error("Modport item is not a function/task: "<<nodep->prettyName());
|
||||
}
|
||||
@@ -1435,6 +1451,7 @@ private:
|
||||
virtual void visit(AstPin* nodep, AstNUser*) {
|
||||
// Pin: Link to submodule's port
|
||||
checkNoDot(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
if (!nodep->modVarp()) {
|
||||
if (!m_pinSymp) nodep->v3fatalSrc("Pin not under cell?\n");
|
||||
VSymEnt* foundp = m_pinSymp->findIdFlat(nodep->name());
|
||||
@@ -1446,9 +1463,9 @@ private:
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
||||
return;
|
||||
}
|
||||
nodep->v3error(LinkDotState::ucfirst(whatp)<<" not found: "<<nodep->prettyName());
|
||||
nodep->v3error(ucfirst(whatp)<<" not found: "<<nodep->prettyName());
|
||||
} else if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) {
|
||||
nodep->v3error(LinkDotState::ucfirst(whatp)<<" is not an in/out/inout/param/interface: "<<nodep->prettyName());
|
||||
nodep->v3error(ucfirst(whatp)<<" is not an in/out/inout/param/interface: "<<nodep->prettyName());
|
||||
} else {
|
||||
nodep->modVarp(refp);
|
||||
if (refp->user5p() && refp->user5p()->castNode()!=nodep) {
|
||||
@@ -1459,7 +1476,6 @@ private:
|
||||
refp->user5p(nodep);
|
||||
}
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
// Early return() above when deleted
|
||||
}
|
||||
|
||||
+34
-3
@@ -61,6 +61,8 @@ private:
|
||||
bool m_needStart; // Need start marker on lower AstParse
|
||||
AstNodeModule* m_valueModp; // If set, move AstVar->valuep() initial values to this module
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstNodeFTask* m_ftaskp; // Current task
|
||||
AstNodeDType* m_dtypep; // Current data type
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
@@ -85,6 +87,14 @@ private:
|
||||
}
|
||||
|
||||
// VISITs
|
||||
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
||||
if (!nodep->user1SetOnce()) { // Process only once.
|
||||
cleanFileline(nodep);
|
||||
m_ftaskp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
m_ftaskp = NULL;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
|
||||
if (!nodep->user1SetOnce()) { // Process only once.
|
||||
cleanFileline(nodep);
|
||||
@@ -95,6 +105,15 @@ private:
|
||||
m_valueModp = upperValueModp;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep, AstNUser*) {
|
||||
if (!nodep->user1SetOnce()) { // Process only once.
|
||||
cleanFileline(nodep);
|
||||
AstNodeDType* upperDtypep = m_dtypep;
|
||||
m_dtypep = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
m_dtypep = upperDtypep;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstEnumItem* nodep, AstNUser*) {
|
||||
// Expand ranges
|
||||
cleanFileline(nodep);
|
||||
@@ -138,7 +157,11 @@ private:
|
||||
// A variable with an = value can be three things:
|
||||
FileLine* fl = nodep->valuep()->fileline();
|
||||
// 1. Parameters and function inputs: It's a default to use if not overridden
|
||||
if (nodep->isParam() || nodep->isInOnly()) {
|
||||
if (nodep->isParam() || (m_ftaskp && nodep->isInOnly())) {
|
||||
}
|
||||
else if (!m_ftaskp && nodep->isInOnly()) {
|
||||
nodep->v3error("Unsupported: Default value on module input: "<<nodep->prettyName());
|
||||
nodep->valuep()->unlinkFrBack()->deleteTree();
|
||||
} // 2. Under modules, it's an initial value to be loaded at time 0 via an AstInitial
|
||||
else if (m_valueModp) {
|
||||
nodep->addNextHere
|
||||
@@ -163,7 +186,13 @@ private:
|
||||
virtual void visit(AstAttrOf* nodep, AstNUser*) {
|
||||
cleanFileline(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
if (nodep->attrType() == AstAttrType::VAR_CLOCK) {
|
||||
if (nodep->attrType() == AstAttrType::DT_PUBLIC) {
|
||||
AstTypedef* typep = nodep->backp()->castTypedef();
|
||||
if (!typep) nodep->v3fatalSrc("Attribute not attached to typedef");
|
||||
typep->attrPublic(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
||||
}
|
||||
else if (nodep->attrType() == AstAttrType::VAR_CLOCK) {
|
||||
if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable");
|
||||
m_varp->attrScClocked(true);
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
||||
@@ -252,7 +281,7 @@ private:
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
return;
|
||||
} else {
|
||||
defp = new AstTypedef(nodep->fileline(), nodep->name(), VFlagChildDType(), dtypep);
|
||||
defp = new AstTypedef(nodep->fileline(), nodep->name(), NULL, VFlagChildDType(), dtypep);
|
||||
m_implTypedef.insert(make_pair(make_pair(nodep->containerp(), defp->name()), defp));
|
||||
backp->addNextHere(defp);
|
||||
}
|
||||
@@ -313,6 +342,8 @@ public:
|
||||
LinkParseVisitor(AstNetlist* rootp) {
|
||||
m_varp = NULL;
|
||||
m_modp = NULL;
|
||||
m_ftaskp = NULL;
|
||||
m_dtypep = NULL;
|
||||
m_inAlways = false;
|
||||
m_inGenerate = false;
|
||||
m_needStart = false;
|
||||
|
||||
@@ -250,8 +250,8 @@ private:
|
||||
void expectFormat(AstNode* nodep, const string& format, AstNode* argp, bool isScan) {
|
||||
// Check display arguments
|
||||
bool inPct = false;
|
||||
for (const char* inp = format.c_str(); *inp; inp++) {
|
||||
char ch = tolower(*inp); // Breaks with iterators...
|
||||
for (string::const_iterator it = format.begin(); it != format.end(); ++it) {
|
||||
char ch = tolower(*it);
|
||||
if (!inPct && ch=='%') {
|
||||
inPct = true;
|
||||
} else if (inPct) {
|
||||
|
||||
@@ -107,6 +107,18 @@ private:
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstMemberDType* nodep, AstNUser*) {
|
||||
if (!nodep->user1()) {
|
||||
rename(nodep, false);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstMemberSel* nodep, AstNUser*) {
|
||||
if (!nodep->user1()) {
|
||||
rename(nodep, false);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScope* nodep, AstNUser*) {
|
||||
if (!nodep->user1SetOnce()) {
|
||||
if (nodep->aboveScopep()) nodep->aboveScopep()->iterate(*this);
|
||||
|
||||
+91
-27
@@ -25,6 +25,7 @@
|
||||
#include <cstdarg>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include "V3Global.h"
|
||||
#include "V3Number.h"
|
||||
|
||||
#define MAX_SPRINTF_DOUBLE_SIZE 100 // Maximum characters with a sprintf %e/%f/%g (probably < 30)
|
||||
@@ -47,6 +48,7 @@ V3Number::V3Number(VerilogString, FileLine* fileline, const string& str) {
|
||||
}
|
||||
}
|
||||
}
|
||||
opCleanThis();
|
||||
}
|
||||
|
||||
V3Number::V3Number (FileLine* fileline, const char* sourcep) {
|
||||
@@ -102,7 +104,7 @@ V3Number::V3Number (FileLine* fileline, const char* sourcep) {
|
||||
}
|
||||
// Otherwise...
|
||||
else if (!m_sized) {
|
||||
width(32, false); // Says the spec.
|
||||
width(32, false); // Says IEEE 1800-2012 5.7.1
|
||||
if (unbased) isSigned(true); // Also says the spec.
|
||||
}
|
||||
|
||||
@@ -252,6 +254,7 @@ V3Number::V3Number (FileLine* fileline, const char* sourcep) {
|
||||
setBit(obit, bitIs(obit-1));
|
||||
obit++;
|
||||
}
|
||||
opCleanThis();
|
||||
|
||||
//printf("Dump \"%s\" CP \"%s\" B '%c' %d W %d\n", sourcep, value_startp, base, width(), m_value[0]);
|
||||
}
|
||||
@@ -276,11 +279,13 @@ V3Number& V3Number::setQuad(vluint64_t value) {
|
||||
for (int i=0; i<words(); i++) m_value[i]=m_valueX[i] = 0;
|
||||
m_value[0] = value & VL_ULL(0xffffffff);
|
||||
m_value[1] = (value>>VL_ULL(32)) & VL_ULL(0xffffffff);
|
||||
opCleanThis();
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::setLong(uint32_t value) {
|
||||
for (int i=0; i<words(); i++) m_value[i]=m_valueX[i] = 0;
|
||||
m_value[0] = value;
|
||||
opCleanThis();
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::setLongS(vlsint32_t value) {
|
||||
@@ -288,6 +293,7 @@ V3Number& V3Number::setLongS(vlsint32_t value) {
|
||||
union { uint32_t u; vlsint32_t s; } u;
|
||||
u.s = value;
|
||||
m_value[0] = u.u;
|
||||
opCleanThis();
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::setDouble(double value) {
|
||||
@@ -314,16 +320,34 @@ V3Number& V3Number::setAllBits0() {
|
||||
}
|
||||
V3Number& V3Number::setAllBits1() {
|
||||
for (int i=0; i<words(); i++) { m_value[i]= ~0; m_valueX[i] = 0; }
|
||||
opCleanThis();
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::setAllBitsX() {
|
||||
// Use setAllBitsXRemoved if calling this based on a non-X/Z input value such as divide by zero
|
||||
for (int i=0; i<words(); i++) { m_value[i]=m_valueX[i] = ~0; }
|
||||
opCleanThis();
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::setAllBitsZ() {
|
||||
for (int i=0; i<words(); i++) { m_value[i]=0; m_valueX[i] = ~0; }
|
||||
opCleanThis();
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::setAllBitsXRemoved() {
|
||||
if (!v3Global.constRemoveXs()) {
|
||||
return setAllBitsX();
|
||||
} else {
|
||||
// If we get a divide by zero we get Xs.
|
||||
// But after V3Unknown we have removed Xs, so use --x-assign to direct-insert 0/1
|
||||
if (v3Global.opt.xAssign() == "1") {
|
||||
return setAllBits1();
|
||||
} else {
|
||||
return setAllBits0();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
V3Number& V3Number::setMask(int nbits) {
|
||||
setZero();
|
||||
for (int bit=0; bit<nbits; bit++) { setBit(bit,1); }
|
||||
@@ -341,6 +365,8 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
|
||||
if (width()!=64) out<<"%E-bad-width-double";
|
||||
else out<<toDouble();
|
||||
return out.str();
|
||||
} else {
|
||||
if ((m_value[words()-1] | m_valueX[words()-1]) & ~hiWordMask()) out<<"%E-hidden-bits";
|
||||
}
|
||||
if (prefixed) {
|
||||
if (sized()) {
|
||||
@@ -751,6 +777,7 @@ V3Number& V3Number::opCountOnes (const V3Number& lhs) {
|
||||
if (lhs.isFourState()) return setAllBitsX();
|
||||
setZero();
|
||||
m_value[0] = lhs.countOnes();
|
||||
opCleanThis();
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::opIsUnknown (const V3Number& lhs) {
|
||||
@@ -891,6 +918,23 @@ V3Number& V3Number::opRepl (const V3Number& lhs, uint32_t rhsval) { // rhs is #
|
||||
return *this;
|
||||
}
|
||||
|
||||
V3Number& V3Number::opStreamL (const V3Number& lhs, const V3Number& rhs) {
|
||||
setZero();
|
||||
// See also error in V3Width
|
||||
if (!lhs.sized()) {
|
||||
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());
|
||||
for (int istart=0; istart<lhs.width(); istart+=ssize) {
|
||||
int ostart=max(0, lhs.width()-ssize-istart);
|
||||
for (int bit=0; bit<ssize && bit<lhs.width()-istart; bit++) {
|
||||
setBit(ostart+bit, lhs.bitIs(istart+bit));
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
V3Number& V3Number::opLogAnd (const V3Number& lhs, const V3Number& rhs) {
|
||||
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation
|
||||
char loutc = 0;
|
||||
@@ -1097,21 +1141,29 @@ V3Number& V3Number::opShiftR (const V3Number& lhs, const V3Number& rhs) {
|
||||
if (rhs.isFourState()) return setAllBitsX();
|
||||
setZero();
|
||||
uint32_t rhsval = rhs.toUInt();
|
||||
for (int bit=0; bit<this->width(); bit++) {
|
||||
setBit(bit,lhs.bitIs(bit + rhsval));
|
||||
if (rhsval < (uint32_t)lhs.width()) {
|
||||
for (int bit=0; bit<this->width(); bit++) {
|
||||
setBit(bit,lhs.bitIs(bit + rhsval));
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
V3Number& V3Number::opShiftRS (const V3Number& lhs, const V3Number& rhs) {
|
||||
V3Number& V3Number::opShiftRS (const V3Number& lhs, const V3Number& rhs, uint32_t lbits) {
|
||||
// L(lhs) bit return
|
||||
// The spec says a unsigned >>> still acts as a normal >>.
|
||||
// We presume it is signed; as that's V3Width's job to convert to opShiftR
|
||||
if (rhs.isFourState()) return setAllBitsX();
|
||||
setZero();
|
||||
uint32_t rhsval = rhs.toUInt();
|
||||
for (int bit=0; bit<this->width(); bit++) {
|
||||
setBit(bit,lhs.bitIsExtend(bit + rhsval));
|
||||
if (rhsval < (uint32_t)lhs.width()) {
|
||||
for (int bit=0; bit<this->width(); bit++) {
|
||||
setBit(bit,lhs.bitIsExtend(bit + rhsval, lbits));
|
||||
}
|
||||
} else {
|
||||
for (int bit=0; bit<this->width(); bit++) {
|
||||
setBit(bit,lhs.bitIs(lbits-1));
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -1212,7 +1264,7 @@ V3Number& V3Number::opDiv (const V3Number& lhs, const V3Number& rhs) {
|
||||
UINFO(9, "opdiv "<<lhs<<" "<<rhs<<endl);
|
||||
// i op j, max(L(lhs),L(rhs)) bit return, if any 4-state, 4-state return
|
||||
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
|
||||
if (rhs.isEqZero()) return setAllBitsX();
|
||||
if (rhs.isEqZero()) return setAllBitsXRemoved();
|
||||
if (lhs.width()<=64) {
|
||||
setQuad(lhs.toUQuad() / rhs.toUQuad());
|
||||
return *this;
|
||||
@@ -1225,7 +1277,7 @@ V3Number& V3Number::opDivS (const V3Number& lhs, const V3Number& rhs) {
|
||||
// Signed divide
|
||||
//UINFO(9, ">>divs-start "<<lhs<<" "<<rhs<<endl);
|
||||
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
|
||||
if (rhs.isEqZero()) return setAllBitsX();
|
||||
if (rhs.isEqZero()) return setAllBitsXRemoved();
|
||||
V3Number lhsNoSign = lhs; if (lhs.isNegative()) lhsNoSign.opNegate(lhs);
|
||||
V3Number rhsNoSign = rhs; if (rhs.isNegative()) rhsNoSign.opNegate(rhs);
|
||||
V3Number qNoSign = opDiv(lhsNoSign,rhsNoSign);
|
||||
@@ -1242,7 +1294,7 @@ V3Number& V3Number::opDivS (const V3Number& lhs, const V3Number& rhs) {
|
||||
V3Number& V3Number::opModDiv (const V3Number& lhs, const V3Number& rhs) {
|
||||
// i op j, max(L(lhs),L(rhs)) bit return, if any 4-state, 4-state return
|
||||
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
|
||||
if (rhs.isEqZero()) return setAllBitsX();
|
||||
if (rhs.isEqZero()) return setAllBitsXRemoved();
|
||||
if (lhs.width()<=64) {
|
||||
setQuad(lhs.toUQuad() % rhs.toUQuad());
|
||||
return *this;
|
||||
@@ -1254,7 +1306,7 @@ V3Number& V3Number::opModDiv (const V3Number& lhs, const V3Number& rhs) {
|
||||
V3Number& V3Number::opModDivS (const V3Number& lhs, const V3Number& rhs) {
|
||||
// Signed moddiv
|
||||
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
|
||||
if (rhs.isEqZero()) return setAllBitsX();
|
||||
if (rhs.isEqZero()) return setAllBitsXRemoved();
|
||||
V3Number lhsNoSign = lhs; if (lhs.isNegative()) lhsNoSign.opNegate(lhs);
|
||||
V3Number rhsNoSign = rhs; if (rhs.isNegative()) rhsNoSign.opNegate(rhs);
|
||||
V3Number qNoSign = opModDiv(lhsNoSign,rhsNoSign);
|
||||
@@ -1291,6 +1343,7 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
|
||||
}
|
||||
UINFO(9, " opmoddiv-1w "<<lhs<<" "<<rhs<<" q="<<*this<<" rem=0x"<<hex<<k<<dec<<endl);
|
||||
if (is_modulus) { setZero(); m_value[0] = k; }
|
||||
opCleanThis();
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1373,21 +1426,33 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
|
||||
m_value[i] = (un[i] >> s) | (shift_mask & (un[i+1] << (32-s)));
|
||||
}
|
||||
for (int i=vw; i<words; i++) m_value[i] = 0;
|
||||
opCleanThis();
|
||||
UINFO(9, " opmoddiv-mod "<<lhs<<" "<<rhs<<" now="<<*this<<endl);
|
||||
return *this;
|
||||
} else { // division
|
||||
opCleanThis();
|
||||
UINFO(9, " opmoddiv-div "<<lhs<<" "<<rhs<<" now="<<*this<<endl);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
V3Number& V3Number::opPow (const V3Number& lhs, const V3Number& rhs) {
|
||||
V3Number& V3Number::opPow (const V3Number& lhs, const V3Number& rhs, bool lsign, bool rsign) {
|
||||
// L(i) bit return, if any 4-state, 4-state return
|
||||
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
|
||||
if (lhs.isEqZero()) return setZero();
|
||||
if (rhs.isEqZero()) return setQuad(1); // Overrides lhs 0 -> return 0
|
||||
// We may want to special case when the lhs is 2, so we can get larger outputs
|
||||
if (lhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large >64bit ** math not implemented yet: "<<*this);
|
||||
if (rhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large >64bit ** math not implemented yet: "<<*this);
|
||||
if (lhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large >64bit ** power operator not implemented yet: "<<*this);
|
||||
if (rhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large >64bit ** power operator not implemented yet: "<<*this);
|
||||
if (rsign && rhs.isNegative()) {
|
||||
if (lhs.isEqZero()) return setAllBitsXRemoved();
|
||||
else if (lhs.isEqOne()) return setQuad(1);
|
||||
else if (lsign && lhs.isEqAllOnes()) {
|
||||
if (rhs.bitIs1(0)) return setAllBits1(); // -1^odd=-1
|
||||
else return setQuad(1); // -1^even=1
|
||||
}
|
||||
return setZero();
|
||||
}
|
||||
if (lhs.isEqZero()) return setZero();
|
||||
setZero();
|
||||
m_value[0] = 1;
|
||||
V3Number power (lhs.m_fileline, width()); power.opAssign(lhs);
|
||||
@@ -1404,14 +1469,14 @@ V3Number& V3Number::opPow (const V3Number& lhs, const V3Number& rhs) {
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::opPowS (const V3Number& lhs, const V3Number& rhs) {
|
||||
// Signed multiply
|
||||
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
|
||||
if (lhs.isEqZero() && rhs.isNegative()) return setAllBitsX(); // Per spec
|
||||
if (!lhs.isNegative() && !rhs.isNegative()) return opPow(lhs,rhs);
|
||||
//if (lhs.isNegative() || rhs.isNonIntegral()) return setAllBitsX(); // Illegal pow() call
|
||||
m_fileline->v3fatalSrc("Unsupported: Power (**) operator with negative numbers: "<<*this);
|
||||
return setAllBitsX();
|
||||
V3Number& V3Number::opPowSU (const V3Number& lhs, const V3Number& rhs) {
|
||||
return opPow(lhs,rhs,true,false);
|
||||
}
|
||||
V3Number& V3Number::opPowSS (const V3Number& lhs, const V3Number& rhs) {
|
||||
return opPow(lhs,rhs,true,true);
|
||||
}
|
||||
V3Number& V3Number::opPowUS (const V3Number& lhs, const V3Number& rhs) {
|
||||
return opPow(lhs,rhs,false,true);
|
||||
}
|
||||
|
||||
V3Number& V3Number::opBufIf1 (const V3Number& ens, const V3Number& if1s) {
|
||||
@@ -1432,11 +1497,11 @@ V3Number& V3Number::opAssign (const V3Number& lhs) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
V3Number& V3Number::opExtendS (const V3Number& lhs) {
|
||||
V3Number& V3Number::opExtendS (const V3Number& lhs, uint32_t lbits) {
|
||||
// Note may be a width change during the sign extension
|
||||
setZero();
|
||||
for(int bit=0; bit<this->width(); bit++) {
|
||||
setBit(bit,lhs.bitIsExtend(bit));
|
||||
setBit(bit,lhs.bitIsExtend(bit, lbits));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -1447,9 +1512,8 @@ V3Number& V3Number::opClean (const V3Number& lhs, uint32_t bits) {
|
||||
|
||||
void V3Number::opCleanThis() {
|
||||
// Clean in place number
|
||||
if (uint32_t okbits = (width() & 31)) {
|
||||
m_value[words()-1] &= ((1UL<<okbits)-1);
|
||||
}
|
||||
m_value[words()-1] &= hiWordMask();
|
||||
m_valueX[words()-1] &= hiWordMask();
|
||||
}
|
||||
|
||||
V3Number& V3Number::opSel (const V3Number& lhs, const V3Number& msb, const V3Number& lsb) {
|
||||
|
||||
+25
-13
@@ -60,17 +60,18 @@ public:
|
||||
}
|
||||
private:
|
||||
char bitIs (int bit) const {
|
||||
if (bit>=m_width) {
|
||||
bit = m_width-1;
|
||||
if (bit>=m_width || bit<0) {
|
||||
// We never sign extend
|
||||
return ( "00zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0)
|
||||
| ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] );
|
||||
return '0';
|
||||
}
|
||||
return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0)
|
||||
| ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); }
|
||||
char bitIsExtend (int bit) const {
|
||||
if (bit>=m_width) {
|
||||
bit = m_width-1;
|
||||
char bitIsExtend (int bit, int lbits) const {
|
||||
// lbits usually = width, but for C optimizations width=32_bits, lbits = 32_or_less
|
||||
if (bit<0) return '0';
|
||||
UASSERT(lbits<=m_width, "Extend of wrong size");
|
||||
if (bit>=lbits) {
|
||||
bit = lbits ? lbits-1 : 0;
|
||||
// We do sign extend
|
||||
return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0)
|
||||
| ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] );
|
||||
@@ -78,22 +79,28 @@ private:
|
||||
return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0)
|
||||
| ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); }
|
||||
bool bitIs0 (int bit) const {
|
||||
if (bit<0) return false;
|
||||
if (bit>=m_width) return !bitIsXZ(m_width-1);
|
||||
return ( (m_value[bit/32] & (1UL<<(bit&31)))==0 && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); }
|
||||
bool bitIs1 (int bit) const {
|
||||
if (bit<0) return false;
|
||||
if (bit>=m_width) return false;
|
||||
return ( (m_value[bit/32] & (1UL<<(bit&31))) && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); }
|
||||
bool bitIs1Extend (int bit) const {
|
||||
if (bit<0) return false;
|
||||
if (bit>=m_width) return bitIs1Extend(m_width-1);
|
||||
return ( (m_value[bit/32] & (1UL<<(bit&31))) && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); }
|
||||
bool bitIsX (int bit) const {
|
||||
if (bit<0) return false;
|
||||
if (bit>=m_width) return bitIsZ(m_width-1);
|
||||
return ( (m_value[bit/32] & (1UL<<(bit&31))) && (m_valueX[bit/32] & (1UL<<(bit&31))) ); }
|
||||
bool bitIsXZ(int bit) const {
|
||||
if (bit<0) return false;
|
||||
if (bit>=m_width) return bitIsXZ(m_width-1);
|
||||
return ( (m_valueX[bit/32] & (1UL<<(bit&31))) && 1);
|
||||
return ( (m_valueX[bit/32] & (1UL<<(bit&31))));
|
||||
}
|
||||
bool bitIsZ (int bit) const {
|
||||
if (bit<0) return false;
|
||||
if (bit>=m_width) return bitIsZ(m_width-1);
|
||||
return ( (~m_value[bit/32] & (1UL<<(bit&31))) && (m_valueX[bit/32] & (1UL<<(bit&31))) ); }
|
||||
uint32_t bitsValue(int lsb, int nbits) const {
|
||||
@@ -103,6 +110,7 @@ private:
|
||||
}
|
||||
|
||||
int words() const { return ((width()+31)/32); }
|
||||
uint32_t hiWordMask() const { return VL_MASK_I(width()); }
|
||||
|
||||
V3Number& opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool is_modulus);
|
||||
|
||||
@@ -111,7 +119,7 @@ public:
|
||||
// CONSTRUCTORS
|
||||
V3Number(FileLine* fileline) { init(fileline, 1); }
|
||||
V3Number(FileLine* fileline, int width) { init(fileline, width); } // 0=unsized
|
||||
V3Number(FileLine* fileline, int width, uint32_t value) { init(fileline, width); m_value[0]=value; }
|
||||
V3Number(FileLine* fileline, int width, uint32_t value) { init(fileline, width); m_value[0]=value; opCleanThis(); }
|
||||
V3Number(FileLine* fileline, const char* source); // Create from a verilog 32'hxxxx number.
|
||||
V3Number(VerilogString, FileLine* fileline, const string& vvalue);
|
||||
|
||||
@@ -137,6 +145,7 @@ public:
|
||||
}
|
||||
|
||||
// SETTERS
|
||||
V3Number& setAllBitsXRemoved();
|
||||
V3Number& setAllBitsX();
|
||||
V3Number& setAllBitsZ();
|
||||
V3Number& setAllBits0();
|
||||
@@ -199,7 +208,7 @@ public:
|
||||
V3Number& opBitsNonZ(const V3Number& lhs); // Z->0, 0/1/X->1
|
||||
//
|
||||
V3Number& opAssign (const V3Number& lhs);
|
||||
V3Number& opExtendS (const V3Number& lhs); // Sign extension
|
||||
V3Number& opExtendS (const V3Number& lhs, uint32_t lbits); // Sign extension
|
||||
V3Number& opRedOr (const V3Number& lhs);
|
||||
V3Number& opRedAnd (const V3Number& lhs);
|
||||
V3Number& opRedXor (const V3Number& lhs);
|
||||
@@ -213,6 +222,7 @@ public:
|
||||
V3Number& opConcat (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opRepl (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opRepl (const V3Number& lhs, uint32_t rhs);
|
||||
V3Number& opStreamL (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opSel (const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
||||
V3Number& opSel (const V3Number& lhs, uint32_t rhs, uint32_t ths);
|
||||
V3Number& opCond (const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
||||
@@ -238,8 +248,10 @@ public:
|
||||
V3Number& opDivS (const V3Number& lhs, const V3Number& rhs); // Signed
|
||||
V3Number& opModDiv (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opModDivS (const V3Number& lhs, const V3Number& rhs); // Signed
|
||||
V3Number& opPow (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opPowS (const V3Number& lhs, const V3Number& rhs); // Signed
|
||||
V3Number& opPow (const V3Number& lhs, const V3Number& rhs, bool lsign=false, bool rsign=false);
|
||||
V3Number& opPowSU (const V3Number& lhs, const V3Number& rhs); // Signed lhs, unsigned rhs
|
||||
V3Number& opPowSS (const V3Number& lhs, const V3Number& rhs); // Signed lhs, signed rhs
|
||||
V3Number& opPowUS (const V3Number& lhs, const V3Number& rhs); // Unsigned lhs, signed rhs
|
||||
V3Number& opAnd (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opChangeXor(const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opXor (const V3Number& lhs, const V3Number& rhs);
|
||||
@@ -248,7 +260,7 @@ public:
|
||||
V3Number& opRotR (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opRotL (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opShiftR (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opShiftRS (const V3Number& lhs, const V3Number& rhs); // Arithmetic w/carry
|
||||
V3Number& opShiftRS (const V3Number& lhs, const V3Number& rhs, uint32_t lbits); // Arithmetic w/carry
|
||||
V3Number& opShiftL (const V3Number& lhs, const V3Number& rhs);
|
||||
// Comparisons
|
||||
V3Number& opEq (const V3Number& lhs, const V3Number& rhs);
|
||||
|
||||
+32
-18
@@ -107,17 +107,27 @@ void V3Options::addLangExt(const string& langext, const V3LangCode& lc) {
|
||||
void V3Options::addLibExtV(const string& libext) {
|
||||
m_impp->addLibExtV(libext);
|
||||
}
|
||||
void V3Options::addDefine(const string& defline) {
|
||||
void V3Options::addDefine(const string& defline, bool allowPlus) {
|
||||
// Split +define+foo=value into the appropriate parts and parse
|
||||
string def = defline;
|
||||
string value;
|
||||
string::size_type pos;
|
||||
if ( ((pos=defline.find("+")) != string::npos)
|
||||
|| ((pos=defline.find("=")) != string::npos)) {
|
||||
value = def.substr(pos+1);
|
||||
def.erase(pos);
|
||||
// Optional + says to allow multiple defines on the line
|
||||
// + is not quotable, as other simulators do not allow that
|
||||
string left = defline;
|
||||
while (left != "") {
|
||||
string def = left;
|
||||
string::size_type pos;
|
||||
if (allowPlus && ((pos=left.find("+")) != string::npos)) {
|
||||
left = left.substr(pos+1);
|
||||
def.erase(pos);
|
||||
} else {
|
||||
left = "";
|
||||
}
|
||||
string value;
|
||||
if ((pos=def.find("=")) != string::npos) {
|
||||
value = def.substr(pos+1);
|
||||
def.erase(pos);
|
||||
}
|
||||
V3PreShell::defineCmdLine(def,value);
|
||||
}
|
||||
V3PreShell::defineCmdLine(def,value);
|
||||
}
|
||||
|
||||
void V3Options::addCppFile(const string& filename) {
|
||||
@@ -591,9 +601,6 @@ string V3Options::getenvVERILATOR_ROOT() {
|
||||
string V3Options::version() {
|
||||
string ver = DTVERSION;
|
||||
ver += " rev "+cvtToStr(DTVERSION_rev);
|
||||
#ifdef NEW_ORDERING
|
||||
ver += " (ord)";
|
||||
#endif
|
||||
return ver;
|
||||
}
|
||||
|
||||
@@ -625,13 +632,14 @@ void V3Options::parseOpts (FileLine* fl, int argc, char** argv) {
|
||||
|
||||
// Default certain options and error check
|
||||
// Detailed error, since this is what we often get when run with minimal arguments
|
||||
if (vFiles().empty()) {
|
||||
const V3StringList& vFilesList = vFiles();
|
||||
if (vFilesList.empty()) {
|
||||
v3fatal("verilator: No Input Verilog file specified on command line, see verilator --help for more information\n");
|
||||
}
|
||||
|
||||
// Default prefix to the filename
|
||||
if (prefix()=="" && topModule()!="") m_prefix = string("V")+topModule();
|
||||
if (prefix()=="") m_prefix = string("V")+filenameNonExt(*(vFiles().begin()));
|
||||
if (prefix()=="" && vFilesList.size()>=1) m_prefix = string("V")+filenameNonExt(*(vFilesList.begin()));
|
||||
if (modPrefix()=="") m_modPrefix = prefix();
|
||||
|
||||
// Find files in makedir
|
||||
@@ -670,7 +678,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
if (argv[i][0]=='+') {
|
||||
char *sw = argv[i];
|
||||
if ( !strncmp (sw, "+define+", 8)) {
|
||||
addDefine (string (sw+strlen("+define+")));
|
||||
addDefine (string (sw+strlen("+define+")), true);
|
||||
}
|
||||
else if ( !strncmp (sw, "+incdir+", 8)) {
|
||||
addIncDirUser (parseFileArg(optdir, string (sw+strlen("+incdir+"))));
|
||||
@@ -715,7 +723,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
else if ( !strcmp (sw, "-E") ) { m_preprocOnly = true; }
|
||||
else if ( onoff (sw, "-MMD", flag/*ref*/) ) { m_makeDepend = flag; }
|
||||
else if ( onoff (sw, "-MP", flag/*ref*/) ) { m_makePhony = flag; }
|
||||
else if ( onoff (sw, "-assert", flag/*ref*/) ) { m_assert = flag; m_psl = flag; }
|
||||
else if ( !strcmp (sw, "-P") ) { m_preprocNoLine = true; }
|
||||
else if ( onoff (sw, "-assert", flag/*ref*/) ) { m_assert = flag; }
|
||||
else if ( onoff (sw, "-autoflush", flag/*ref*/) ) { m_autoflush = flag; }
|
||||
else if ( onoff (sw, "-bbox-sys", flag/*ref*/) ) { m_bboxSys = flag; }
|
||||
else if ( onoff (sw, "-bbox-unsup", flag/*ref*/) ) { m_bboxUnsup = flag; }
|
||||
@@ -745,7 +754,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
else if ( onoff (sw, "-pins-uint8", flag/*ref*/) ){ m_pinsUint8 = flag; }
|
||||
else if ( !strcmp (sw, "-private") ) { m_public = false; }
|
||||
else if ( onoff (sw, "-profile-cfuncs", flag/*ref*/) ) { m_profileCFuncs = flag; }
|
||||
else if ( onoff (sw, "-psl", flag/*ref*/) ) { m_psl = flag; }
|
||||
else if ( onoff (sw, "-psl-deprecated", flag/*ref*/) ) { m_psl = flag; } // Undocumented
|
||||
else if ( onoff (sw, "-public", flag/*ref*/) ) { m_public = flag; }
|
||||
else if ( onoff (sw, "-report-unoptflat", flag/*ref*/) ) { m_reportUnoptflat = flag; }
|
||||
else if ( onoff (sw, "-savable", flag/*ref*/) ) { m_savable = flag; }
|
||||
@@ -756,6 +765,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
else if ( !strcmp (sw, "-sv") ) { m_defaultLanguage = V3LangCode::L1800_2005; }
|
||||
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; }
|
||||
else if ( onoff (sw, "-trace-structs", flag/*ref*/) ) { m_traceStructs = flag; }
|
||||
else if ( onoff (sw, "-trace-underscore", flag/*ref*/) ) { m_traceUnderscore = flag; }
|
||||
else if ( onoff (sw, "-underline-zero", flag/*ref*/) ) { m_underlineZero = flag; } // Undocumented, old Verilator-2
|
||||
@@ -774,6 +784,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
case 'b': m_oCombine = flag; break;
|
||||
case 'c': m_oConst = flag; break;
|
||||
case 'd': m_oDedupe = flag; break;
|
||||
case 'm': m_oAssemble = flag; break;
|
||||
case 'e': m_oCase = flag; break;
|
||||
case 'f': m_oFlopGater = flag; break;
|
||||
case 'g': m_oGate = flag; break;
|
||||
@@ -802,7 +813,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
m_convergeLimit = atoi(argv[i]);
|
||||
}
|
||||
else if ( !strncmp (sw, "-D", 2)) {
|
||||
addDefine (string (sw+strlen("-D")));
|
||||
addDefine (string (sw+strlen("-D")), false);
|
||||
}
|
||||
else if ( !strcmp (sw, "-debug") ) {
|
||||
setDebugMode(3);
|
||||
@@ -1214,6 +1225,7 @@ V3Options::V3Options() {
|
||||
m_pinsBv = 65;
|
||||
m_profileCFuncs = false;
|
||||
m_preprocOnly = false;
|
||||
m_preprocNoLine = false;
|
||||
m_psl = false;
|
||||
m_public = false;
|
||||
m_savable = false;
|
||||
@@ -1223,6 +1235,7 @@ V3Options::V3Options() {
|
||||
m_systemPerl = false;
|
||||
m_trace = false;
|
||||
m_traceDups = false;
|
||||
m_traceParams = true;
|
||||
m_traceStructs = false;
|
||||
m_traceUnderscore = false;
|
||||
m_underlineZero = false;
|
||||
@@ -1317,6 +1330,7 @@ void V3Options::optimize(int level) {
|
||||
m_oSubstConst = flag;
|
||||
m_oTable = flag;
|
||||
m_oDedupe = flag;
|
||||
m_oAssemble = flag;
|
||||
// And set specific optimization levels
|
||||
if (level >= 3) {
|
||||
m_inlineMult = -1; // Maximum inlining
|
||||
|
||||
+7
-1
@@ -59,6 +59,7 @@ class V3Options {
|
||||
bool m_preprocOnly; // main switch: -E
|
||||
bool m_makeDepend; // main switch: -MMD
|
||||
bool m_makePhony; // main switch: -MP
|
||||
bool m_preprocNoLine;// main switch: -P
|
||||
bool m_assert; // main switch: --assert
|
||||
bool m_autoflush; // main switch: --autoflush
|
||||
bool m_bboxSys; // main switch: --bbox-sys
|
||||
@@ -90,6 +91,7 @@ class V3Options {
|
||||
bool m_stats; // main switch: --stats
|
||||
bool m_trace; // main switch: --trace
|
||||
bool m_traceDups; // main switch: --trace-dups
|
||||
bool m_traceParams; // main switch: --trace-params
|
||||
bool m_traceStructs; // main switch: --trace-structs
|
||||
bool m_traceUnderscore;// main switch: --trace-underscore
|
||||
bool m_underlineZero;// main switch: --underline-zero; undocumented old Verilator 2
|
||||
@@ -137,6 +139,7 @@ class V3Options {
|
||||
bool m_oCombine; // main switch: -Ob: common icode packing
|
||||
bool m_oConst; // main switch: -Oc: constant folding
|
||||
bool m_oDedupe; // main switch: -Od: logic deduplication
|
||||
bool m_oAssemble; // main switch: -Om: assign assemble
|
||||
bool m_oExpand; // main switch: -Ox: expansion of C macros
|
||||
bool m_oFlopGater; // main switch: -Of: flop gater detection
|
||||
bool m_oGate; // main switch: -Og: gate wire elimination
|
||||
@@ -153,7 +156,7 @@ class V3Options {
|
||||
private:
|
||||
// METHODS
|
||||
void addArg(const string& flag);
|
||||
void addDefine(const string& defline);
|
||||
void addDefine(const string& defline, bool allowPlus);
|
||||
void addFuture(const string& flag);
|
||||
void addIncDirUser(const string& incdir); // User requested
|
||||
void addIncDirFallback(const string& incdir); // Low priority if not found otherwise
|
||||
@@ -191,6 +194,7 @@ class V3Options {
|
||||
bool preprocOnly() const { return m_preprocOnly; }
|
||||
bool makeDepend() const { return m_makeDepend; }
|
||||
bool makePhony() const { return m_makePhony; }
|
||||
bool preprocNoLine() const { return m_preprocNoLine; }
|
||||
bool underlineZero() const { return m_underlineZero; }
|
||||
string bin() const { return m_bin; }
|
||||
string flags() const { return m_flags; }
|
||||
@@ -215,6 +219,7 @@ class V3Options {
|
||||
bool exe() const { return m_exe; }
|
||||
bool trace() const { return m_trace; }
|
||||
bool traceDups() const { return m_traceDups; }
|
||||
bool traceParams() const { return m_traceParams; }
|
||||
bool traceStructs() const { return m_traceStructs; }
|
||||
bool traceUnderscore() const { return m_traceUnderscore; }
|
||||
bool orderClockDly() const { return m_orderClockDly; }
|
||||
@@ -278,6 +283,7 @@ class V3Options {
|
||||
bool oCombine() const { return m_oCombine; }
|
||||
bool oConst() const { return m_oConst; }
|
||||
bool oDedupe() const { return m_oDedupe; }
|
||||
bool oAssemble() const { return m_oAssemble; }
|
||||
bool oExpand() const { return m_oExpand; }
|
||||
bool oFlopGater() const { return m_oFlopGater; }
|
||||
bool oGate() const { return m_oGate; }
|
||||
|
||||
+65
-433
@@ -63,16 +63,6 @@
|
||||
// the generated signal to become a primary input again.
|
||||
//
|
||||
//
|
||||
#ifdef NEW_ORDERING
|
||||
// Determine nodes that form loops within combo logic
|
||||
// (Strongly connected ignoring assign post/pre's)
|
||||
// Make a subgraph for each loop of combo logic
|
||||
// Break circular logic in each subgraph
|
||||
//
|
||||
// Determine nodes that form loops within a single eval call
|
||||
// Make a subgraph for each loop of sequential logic
|
||||
// Break circular logic in each subgraph
|
||||
#endif
|
||||
//
|
||||
// Rank the graph starting at INPUTS (see V3Graph)
|
||||
//
|
||||
@@ -349,7 +339,8 @@ private:
|
||||
varscp->user1p(newup);
|
||||
}
|
||||
OrderUser* up = (OrderUser*)(varscp->user1p());
|
||||
return up->newVarUserVertex(&m_graph, m_scopep, varscp, type, createdp);
|
||||
OrderVarVertex* varVxp = up->newVarUserVertex(&m_graph, m_scopep, varscp, type, createdp);
|
||||
return varVxp;
|
||||
}
|
||||
|
||||
V3GraphEdge* findEndEdge(V3GraphVertex* vertexp, AstNode* errnodep, OrderLoopEndVertex*& evertexpr) {
|
||||
@@ -366,14 +357,6 @@ private:
|
||||
}
|
||||
|
||||
void process();
|
||||
#ifdef NEW_ORDERING
|
||||
void processLoops(string stepName, V3EdgeFuncP edgeFuncp);
|
||||
void processInsLoop();
|
||||
void processInsLoopEdge(V3GraphEdge* edgep);
|
||||
void processInsLoopNewEdge(V3GraphEdge* oldEdgep, V3GraphVertex* newFromp, V3GraphVertex* newTop);
|
||||
OrderVarVertex* processInsLoopNewVar(OrderVarVertex* oldVertexp, bool& createdr);
|
||||
void processBrokeLoop();
|
||||
#endif
|
||||
void processCircular();
|
||||
typedef deque<OrderEitherVertex*> VertexVec;
|
||||
void processInputs();
|
||||
@@ -414,41 +397,56 @@ private:
|
||||
|
||||
void nodeMarkCircular(OrderVarVertex* vertexp, OrderEdge* edgep) {
|
||||
AstVarScope* nodep = vertexp->varScp();
|
||||
nodep->circular(true);
|
||||
++m_statCut[vertexp->type()];
|
||||
if (edgep) ++m_statCut[edgep->type()];
|
||||
if (vertexp->isClock()) {
|
||||
// Seems obvious; no warning yet
|
||||
//nodep->v3warn(GENCLK,"Signal unoptimizable: Generated clock: "<<nodep->prettyName());
|
||||
} else if (nodep->varp()->isSigPublic()) {
|
||||
nodep->v3warn(UNOPT,"Signal unoptimizable: Feedback to public clock or circular logic: "<<nodep->prettyName());
|
||||
if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPT)) {
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNOPT, true); // Complain just once
|
||||
// Give the user an example.
|
||||
bool tempWeight = (edgep && edgep->weight()==0);
|
||||
if (tempWeight) edgep->weight(1); // Else the below loop detect can't see the loop
|
||||
m_graph.reportLoops(&OrderEdge::followComboConnected, vertexp); // calls OrderGraph::loopsVertexCb
|
||||
if (tempWeight) edgep->weight(0);
|
||||
}
|
||||
} else {
|
||||
// We don't use UNOPT, as there are lots of V2 places where it was needed, that aren't any more
|
||||
// First v3warn not inside warnIsOff so we can see the suppressions with --debug
|
||||
nodep->v3warn(UNOPTFLAT,"Signal unoptimizable: Feedback to clock or circular logic: "<<nodep->prettyName());
|
||||
if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPTFLAT)) {
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNOPTFLAT, true); // Complain just once
|
||||
// Give the user an example.
|
||||
bool tempWeight = (edgep && edgep->weight()==0);
|
||||
if (tempWeight) edgep->weight(1); // Else the below loop detect can't see the loop
|
||||
m_graph.reportLoops(&OrderEdge::followComboConnected, vertexp); // calls OrderGraph::loopsVertexCb
|
||||
if (tempWeight) edgep->weight(0);
|
||||
if (v3Global.opt.reportUnoptflat()) {
|
||||
// Report candidate variables for splitting
|
||||
reportLoopVars(vertexp);
|
||||
// Do a subgraph for the UNOPTFLAT loop
|
||||
OrderGraph loopGraph;
|
||||
m_graph.subtreeLoops(&OrderEdge::followComboConnected,
|
||||
vertexp, &loopGraph);
|
||||
loopGraph.dumpDotFilePrefixedAlways("unoptflat");
|
||||
OrderLogicVertex* fromLVtxp = NULL;
|
||||
OrderLogicVertex* toLVtxp = NULL;
|
||||
if (edgep) {
|
||||
fromLVtxp = dynamic_cast<OrderLogicVertex*>(edgep->fromp());
|
||||
toLVtxp = dynamic_cast<OrderLogicVertex*>(edgep->top());
|
||||
}
|
||||
//
|
||||
if ((fromLVtxp && fromLVtxp->nodep()->castInitial())
|
||||
|| (toLVtxp && toLVtxp->nodep()->castInitial())) {
|
||||
// IEEE does not specify ordering between initial blocks, so we can do whatever we want
|
||||
// We especially do not want to evaluate multiple times, so do not mark the edge circular
|
||||
}
|
||||
else {
|
||||
nodep->circular(true);
|
||||
++m_statCut[vertexp->type()];
|
||||
if (edgep) ++m_statCut[edgep->type()];
|
||||
//
|
||||
if (vertexp->isClock()) {
|
||||
// Seems obvious; no warning yet
|
||||
//nodep->v3warn(GENCLK,"Signal unoptimizable: Generated clock: "<<nodep->prettyName());
|
||||
} else if (nodep->varp()->isSigPublic()) {
|
||||
nodep->v3warn(UNOPT,"Signal unoptimizable: Feedback to public clock or circular logic: "<<nodep->prettyName());
|
||||
if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPT)) {
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNOPT, true); // Complain just once
|
||||
// Give the user an example.
|
||||
bool tempWeight = (edgep && edgep->weight()==0);
|
||||
if (tempWeight) edgep->weight(1); // Else the below loop detect can't see the loop
|
||||
m_graph.reportLoops(&OrderEdge::followComboConnected, vertexp); // calls OrderGraph::loopsVertexCb
|
||||
if (tempWeight) edgep->weight(0);
|
||||
}
|
||||
} else {
|
||||
// We don't use UNOPT, as there are lots of V2 places where it was needed, that aren't any more
|
||||
// First v3warn not inside warnIsOff so we can see the suppressions with --debug
|
||||
nodep->v3warn(UNOPTFLAT,"Signal unoptimizable: Feedback to clock or circular logic: "<<nodep->prettyName());
|
||||
if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPTFLAT)) {
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNOPTFLAT, true); // Complain just once
|
||||
// Give the user an example.
|
||||
bool tempWeight = (edgep && edgep->weight()==0);
|
||||
if (tempWeight) edgep->weight(1); // Else the below loop detect can't see the loop
|
||||
m_graph.reportLoops(&OrderEdge::followComboConnected, vertexp); // calls OrderGraph::loopsVertexCb
|
||||
if (tempWeight) edgep->weight(0);
|
||||
if (v3Global.opt.reportUnoptflat()) {
|
||||
// Report candidate variables for splitting
|
||||
reportLoopVars(vertexp);
|
||||
// Do a subgraph for the UNOPTFLAT loop
|
||||
OrderGraph loopGraph;
|
||||
m_graph.subtreeLoops(&OrderEdge::followComboConnected,
|
||||
vertexp, &loopGraph);
|
||||
loopGraph.dumpDotFilePrefixedAlways("unoptflat");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -573,12 +571,7 @@ private:
|
||||
UINFO(5," DeleteDomain = "<<m_deleteDomainp<<endl);
|
||||
// Base vertices
|
||||
m_activeSenVxp = NULL;
|
||||
#ifdef NEW_ORDERING
|
||||
m_inputsVxp = new OrderInputsVertex(&m_graph, m_comboDomainp);
|
||||
m_settleVxp = new OrderSettleVertex(&m_graph, m_settleDomainp);
|
||||
#else
|
||||
m_inputsVxp = new OrderInputsVertex(&m_graph, NULL);
|
||||
#endif
|
||||
//
|
||||
nodep->iterateChildren(*this);
|
||||
// Done topscope, erase extra user information
|
||||
@@ -603,7 +596,6 @@ private:
|
||||
}
|
||||
virtual void visit(AstActive* nodep, AstNUser*) {
|
||||
// Create required activation blocks and add to module
|
||||
if (nodep->hasInitial()) return; // Ignore initials
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
m_activep = nodep;
|
||||
m_activeSenVxp = NULL;
|
||||
@@ -666,32 +658,7 @@ private:
|
||||
|| m_inPost
|
||||
) {
|
||||
// Combo logic
|
||||
if (
|
||||
#ifdef NEW_ORDERING
|
||||
m_activep && m_activep->hasSettle()
|
||||
#else
|
||||
0
|
||||
#endif
|
||||
) {
|
||||
// Inside settlement; we use special variable names to prevent
|
||||
// having extra logic to break arcs within
|
||||
#ifdef NEW_ORDERING
|
||||
if (gen) {
|
||||
// Add edge logic_vertex->logic_generated_var
|
||||
bool created = false;
|
||||
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_SETL, &created);
|
||||
new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp);
|
||||
if (created) new OrderEdge(&m_graph, m_settleVxp, varVxp, WEIGHT_INPUT);
|
||||
}
|
||||
if (con) {
|
||||
// Add edge logic_consumed_var->logic_vertex
|
||||
bool created = false;
|
||||
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_SETL, &created);
|
||||
new OrderEdge(&m_graph, varVxp, m_logicVxp, WEIGHT_MEDIUM);
|
||||
if (created) new OrderEdge(&m_graph, m_settleVxp, varVxp, WEIGHT_INPUT);
|
||||
}
|
||||
#endif // NEW_ORDERING
|
||||
} else { // not settle and (combo or inPost)
|
||||
{ // not settle and (combo or inPost)
|
||||
if (gen) {
|
||||
// Add edge logic_vertex->logic_generated_var
|
||||
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD);
|
||||
@@ -806,6 +773,11 @@ private:
|
||||
virtual void visit(AstCoverToggle* nodep, AstNUser*) {
|
||||
iterateNewStmt(nodep);
|
||||
}
|
||||
virtual void visit(AstInitial* nodep, AstNUser*) {
|
||||
// We use initials to setup parameters and static consts's which may be referenced
|
||||
// in user initial blocks. So use ordering to sort them all out.
|
||||
iterateNewStmt(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc*, AstNUser*) {
|
||||
// Ignore for now
|
||||
// We should detect what variables are set in the function, and make
|
||||
@@ -858,212 +830,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Pre-Loop elimination
|
||||
|
||||
#ifdef NEW_ORDERING
|
||||
void OrderVisitor::processInsLoop() {
|
||||
// Input is graph with color() reflecting the sub-graphs we need
|
||||
// to loop remove. Take all the I/O from each subgraph and route
|
||||
// through a new begin/end vertex
|
||||
// Note we DON'T only do certain edges; we want all edges to be preserved,
|
||||
// we'll select which are important later on.
|
||||
|
||||
uint32_t maxcolor = 0;
|
||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
if (maxcolor <= vertexp->color()) maxcolor = vertexp->color() + 1;
|
||||
if (OrderVarVertex* vvertexp = dynamic_cast<OrderVarVertex*>(vertexp)) {
|
||||
vvertexp->pilNewVertexp(NULL); // Clear user-ish information before loop
|
||||
}
|
||||
}
|
||||
|
||||
m_pmlLoopEndps.clear();
|
||||
m_pmlLoopEndps.reserve(maxcolor);
|
||||
m_pmlLoopEndps.assign(maxcolor,NULL);
|
||||
|
||||
m_graph.userClearVertices(); // Vertex::user() // true if added as begin/end
|
||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
if (uint32_t loopColor = vertexp->color()) {
|
||||
OrderEitherVertex* evertexp = dynamic_cast<OrderEitherVertex*>(vertexp);
|
||||
if (!evertexp) v3fatalSrc("Non either vertex found");
|
||||
if (!vertexp->user()) {
|
||||
UINFO(8," pil:nowInLoop lp="<<m_loopIdMax<<" waslp="
|
||||
<<evertexp->inLoop()<<" "<<evertexp<<endl);
|
||||
if (!m_pmlLoopEndps[loopColor]) {
|
||||
AstUntilStable* untilp = new AstUntilStable(m_topScopep->fileline(), NULL, NULL);
|
||||
m_topScopep->addStmtsp(untilp);
|
||||
OrderLoopBeginVertex* beginp
|
||||
= new OrderLoopBeginVertex(&m_graph, m_scopetopp,
|
||||
evertexp->domainp(),
|
||||
untilp,
|
||||
m_loopIdMax, loopColor);
|
||||
m_loopIdMax = (OrderLoopId)(m_loopIdMax+1);
|
||||
UASSERT(LOOPID_MAX>m_loopIdMax, "loopid overflow "<<m_loopIdMax<<" "<<LOOPID_MAX);
|
||||
OrderLoopEndVertex* endp = new OrderLoopEndVertex(&m_graph, beginp);
|
||||
new OrderEdge(&m_graph, beginp, endp, WEIGHT_LOOPBE);
|
||||
m_pmlLoopEndps[loopColor] = endp;
|
||||
// Color edges to belong to subgraph
|
||||
beginp->color(loopColor);
|
||||
endp->color(loopColor);
|
||||
beginp->user(1); // Added; don't iterate on it
|
||||
endp->user(1); // Added; don't iterate on it
|
||||
}
|
||||
if (evertexp->inLoop()) {
|
||||
// Adding node to loop, but already in loop...
|
||||
// Ok if the nodes were a combo loop and now become a sequent loop,
|
||||
// The combo loop will be "under" the sequent loop, so keep the combo #.
|
||||
//UINFO(9, "Adding node to loop, but already in loop: "<<evertexp->name()<<endl);
|
||||
} else {
|
||||
// Make all nodes point from/to the begin/end nodes
|
||||
OrderLoopEndVertex* endp = m_pmlLoopEndps[loopColor];
|
||||
OrderLoopBeginVertex* beginp = endp->beginVertexp();
|
||||
new OrderEdge(&m_graph, beginp, vertexp, WEIGHT_LOOPBE);
|
||||
new OrderEdge(&m_graph, vertexp, endp, WEIGHT_LOOPBE);
|
||||
evertexp->inLoop(beginp->loopId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_graph.userClearEdges(); // Edge::user() // true if we added it
|
||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
if (vertexp->color()) {
|
||||
for (V3GraphEdge* nextp,* edgep = vertexp->outBeginp(); edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp(); // Func may edit the list
|
||||
if (edgep->weight()) {
|
||||
processInsLoopEdge(edgep);
|
||||
} else { // No purpose to this edge any longer
|
||||
edgep->unlinkDelete(); edgep=NULL; // remove old edge
|
||||
}
|
||||
}
|
||||
for (V3GraphEdge* nextp,* edgep = vertexp->inBeginp(); edgep; edgep = nextp) {
|
||||
nextp = edgep->inNextp(); // Func may edit the list
|
||||
if (edgep->weight()) {
|
||||
processInsLoopEdge(edgep);
|
||||
} else { // No purpose to this edge any longer
|
||||
edgep->unlinkDelete(); edgep=NULL; // remove old edge
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Done with space
|
||||
m_pmlLoopEndps.clear();
|
||||
}
|
||||
|
||||
void OrderVisitor::processInsLoopNewEdge(V3GraphEdge* oldEdgep, V3GraphVertex* newFromp, V3GraphVertex* newTop) {
|
||||
// Create new edge based on the old edge's parameters
|
||||
const OrderEdge* oedgep = dynamic_cast<const OrderEdge*>(oldEdgep);
|
||||
V3GraphEdge* newEdgep = oedgep->clone(&m_graph, newFromp, newTop);
|
||||
newEdgep->user(1); // New edge doesn't need to be changed
|
||||
}
|
||||
|
||||
OrderVarVertex* OrderVisitor::processInsLoopNewVar(OrderVarVertex* oldVertexp, bool& createdr) {
|
||||
// Create new VarVertex from given template
|
||||
if (oldVertexp->pilNewVertexp()) {
|
||||
createdr = false;
|
||||
} else {
|
||||
OrderVarVertex* newVertexp = oldVertexp->clone(&m_graph);
|
||||
oldVertexp->pilNewVertexp(newVertexp);
|
||||
createdr = true;
|
||||
}
|
||||
return oldVertexp->pilNewVertexp();
|
||||
}
|
||||
|
||||
void OrderVisitor::processInsLoopEdge(V3GraphEdge* oldEdgep) {
|
||||
if (!oldEdgep->user()) { // if not processed
|
||||
oldEdgep->user(1); // mark as processed
|
||||
if (oldEdgep->fromp()->color() == oldEdgep->top()->color()) {
|
||||
return; // Doesn't cross subgraphs
|
||||
} else {
|
||||
V3GraphVertex* newFromp = oldEdgep->fromp();
|
||||
V3GraphVertex* newTop = oldEdgep->top();
|
||||
//UINFO(6, " pile: "<<newFromp<<" -> "<<newTop<<endl);
|
||||
if (uint32_t fromId = oldEdgep->fromp()->color()) {
|
||||
// Change to come from end block
|
||||
OrderLoopEndVertex* endp = m_pmlLoopEndps[fromId];
|
||||
newFromp = endp;
|
||||
// If it goes from(to) a cutable VarVertex inside the Begin/End block,
|
||||
// we can't lose the variable, as we might need to cut that variable out
|
||||
// in the next pass of processLoops, and processBrokeLoops needs the var pointer.
|
||||
// We'll make another VarVertex (dup of the one "inside" the loop)
|
||||
// and point to it.
|
||||
if (oldEdgep->cutable()) {
|
||||
if (OrderVarVertex* vvFromp = dynamic_cast<OrderVarVertex*>(oldEdgep->fromp())) {
|
||||
// end => newvarvtx -> {whatever}
|
||||
bool created;
|
||||
newFromp = processInsLoopNewVar(vvFromp, created/*ref*/);
|
||||
if (created) processInsLoopNewEdge(oldEdgep, endp, newFromp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uint32_t toId = oldEdgep->top()->color()) {
|
||||
// Change to go to begin block
|
||||
OrderLoopEndVertex* endp = m_pmlLoopEndps[toId];
|
||||
OrderLoopBeginVertex* beginp = endp->beginVertexp();
|
||||
newTop = beginp;
|
||||
// Ditto above
|
||||
if (oldEdgep->cutable()) {
|
||||
if (OrderVarVertex* vvTop = dynamic_cast<OrderVarVertex*>(oldEdgep->top())) {
|
||||
// oldfrom -> newvarvtx => begin
|
||||
bool created;
|
||||
newTop = processInsLoopNewVar(vvTop, created/*ref*/);
|
||||
if (created) processInsLoopNewEdge(oldEdgep, newTop, beginp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New edge with appropriate to/from
|
||||
processInsLoopNewEdge(oldEdgep, newFromp, newTop);
|
||||
oldEdgep->unlinkDelete(); oldEdgep=NULL; // remove old edge
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // NEW_ORDERING
|
||||
|
||||
//######################################################################
|
||||
// Pre-Loop elimination
|
||||
|
||||
#ifdef NEW_ORDERING
|
||||
void OrderVisitor::processBrokeLoop() {
|
||||
// Find those loops that were broken
|
||||
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
// Above processInsLoopEdge makes sure there's always a OrderVarVertex on
|
||||
// boundaries to/from LoopBegin/EndVertex'es
|
||||
if (OrderVarVertex* vvertexp = dynamic_cast<OrderVarVertex*>(vertexp)) {
|
||||
// Any cut edges?
|
||||
bool anyCut = false;
|
||||
for (V3GraphEdge* nextp,* edgep = vertexp->inBeginp(); edgep; edgep = nextp) {
|
||||
nextp = edgep->inNextp(); // We may edit the list
|
||||
if (edgep->weight()==0) { // was cut
|
||||
anyCut = true;
|
||||
edgep->unlinkDelete(); edgep=NULL; // remove old edge
|
||||
}
|
||||
}
|
||||
for (V3GraphEdge* nextp,* edgep = vertexp->outBeginp(); edgep; edgep = nextp) {
|
||||
nextp = edgep->outNextp(); // We may edit the list
|
||||
if (edgep->weight()==0) { // was cut
|
||||
anyCut = true;
|
||||
edgep->unlinkDelete(); edgep=NULL; // remove old edge
|
||||
}
|
||||
}
|
||||
if (anyCut) {
|
||||
UINFO(6," pbl: Cut "<<vertexp->name()<<endl);
|
||||
nodeMarkCircular(vvertexp, NULL);
|
||||
OrderLoopEndVertex* endp = NULL; // Set below in findEndEdge
|
||||
V3GraphEdge* endedgep = findEndEdge(vertexp, vvertexp->varScp(), endp/*ref*/);
|
||||
// Add edge to graphically indicate change detect required
|
||||
endedgep->unlinkDelete(); endedgep=NULL; // remove old edge
|
||||
new OrderChangeDetEdge(&m_graph, vvertexp, endp);
|
||||
// Add variable dependency to until loop
|
||||
AstUntilStable* untilp = endp->beginVertexp()->untilp();
|
||||
untilp->addStablesp(new AstVarRef(vvertexp->varScp()->fileline(), vvertexp->varScp(), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // NEW_ORDERING
|
||||
|
||||
//######################################################################
|
||||
// Clock propagation
|
||||
|
||||
@@ -1146,7 +912,6 @@ void OrderVisitor::processInputsOutIterate(OrderEitherVertex* vertexp, VertexVec
|
||||
//######################################################################
|
||||
// Circular detection
|
||||
|
||||
#ifndef NEW_ORDERING // *NOT* new ordering
|
||||
void OrderVisitor::processCircular() {
|
||||
// Take broken edges and add circular flags
|
||||
// The change detect code will use this to force changedets
|
||||
@@ -1193,7 +958,6 @@ void OrderVisitor::processCircular() {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void OrderVisitor::processSensitive() {
|
||||
// Sc sensitives are required on all inputs that go to a combo
|
||||
@@ -1238,11 +1002,9 @@ void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) {
|
||||
OrderVarVertex* vvertexp = dynamic_cast<OrderVarVertex*>(vertexp);
|
||||
AstSenTree* domainp = NULL;
|
||||
UASSERT(m_comboDomainp, "not preset");
|
||||
#ifndef NEW_ORDERING // New ordering set the input node as combo, so this happens automatically
|
||||
if (vvertexp && vvertexp->varScp()->varp()->isInput()) {
|
||||
domainp = m_comboDomainp;
|
||||
}
|
||||
#endif
|
||||
if (vvertexp && vvertexp->varScp()->isCircular()) {
|
||||
domainp = m_comboDomainp;
|
||||
}
|
||||
@@ -1250,13 +1012,12 @@ void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) {
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
OrderEitherVertex* fromVertexp = (OrderEitherVertex*)edgep->fromp();
|
||||
if (edgep->weight()
|
||||
#ifndef NEW_ORDERING
|
||||
&& fromVertexp->domainMatters()
|
||||
#endif
|
||||
) {
|
||||
UINFO(9," from d="<<(void*)fromVertexp->domainp()<<" "<<fromVertexp<<endl);
|
||||
if (!domainp // First input to this vertex
|
||||
|| domainp->hasSettle()) { // or, we can ignore being in the settle domain
|
||||
|| domainp->hasSettle() // or, we can ignore being in the settle domain
|
||||
|| domainp->hasInitial()) {
|
||||
domainp = fromVertexp->domainp();
|
||||
}
|
||||
else if (domainp->hasCombo()) {
|
||||
@@ -1266,7 +1027,8 @@ void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) {
|
||||
// Any combo input means this vertex must remain combo
|
||||
domainp = m_comboDomainp;
|
||||
}
|
||||
else if (fromVertexp->domainp()->hasSettle()) {
|
||||
else if (fromVertexp->domainp()->hasSettle()
|
||||
|| fromVertexp->domainp()->hasInitial()) {
|
||||
// Ignore that we have a constant (initial) input
|
||||
}
|
||||
else if (domainp != fromVertexp->domainp()) {
|
||||
@@ -1323,7 +1085,7 @@ void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) {
|
||||
void OrderVisitor::processEdgeReport() {
|
||||
// Make report of all signal names and what clock edges they have
|
||||
string filename = v3Global.debugFilename("order_edges.txt");
|
||||
const auto_ptr<ofstream> logp (V3File::new_ofstream(filename));
|
||||
const VL_UNIQUE_PTR<ofstream> logp (V3File::new_ofstream(filename));
|
||||
if (logp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
//Testing emitter: V3EmitV::verilogForTree(v3Global.rootp(), *logp);
|
||||
|
||||
@@ -1426,69 +1188,17 @@ void OrderVisitor::processMove() {
|
||||
|
||||
// New domain... another loop
|
||||
UINFO(5," MoveIterate\n");
|
||||
#ifdef NEW_ORDERING
|
||||
OrderMoveDomScope* domScopep = NULL; // Currently active domain/scope
|
||||
while (!m_pomReadyDomScope.empty()) {
|
||||
// Always need to reamin in same loop construct
|
||||
OrderLoopId curLoop = processMoveLoopCurrent();
|
||||
// Scan list to find search candidates
|
||||
OrderMoveDomScope* loopHuntp = NULL; // Found domscope under same loop
|
||||
OrderMoveDomScope* domHuntp = NULL; // Found domscope under same domain
|
||||
OrderMoveDomScope* scopeHuntp = NULL; // Found domscope under same scope
|
||||
if (domScopep) { UINFO(6," MoveSearch: loop="<<curLoop<<" "<<*domScopep<<endl); }
|
||||
else { UINFO(6," MoveSearch: loop="<<curLoop<<" NULL"<<endl); }
|
||||
for (OrderMoveDomScope* huntp = m_pomReadyDomScope.begin(); huntp; huntp = huntp->readyDomScopeNextp()) {
|
||||
if (huntp->inLoop() == curLoop) {
|
||||
if (!loopHuntp) loopHuntp = huntp;
|
||||
if (domScopep && huntp->domainp() == domScopep->domainp()) {
|
||||
if (!domHuntp) domHuntp = huntp;
|
||||
if (domScopep && huntp->scopep() == domScopep->scopep()) {
|
||||
if (!scopeHuntp) scopeHuntp = huntp;
|
||||
break; // Exact match; all we can hope for
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Recompute the next domScopep to process
|
||||
if (scopeHuntp) {
|
||||
domScopep = scopeHuntp;
|
||||
UINFO(6," MoveIt: SameScope "<<*domScopep<<endl);
|
||||
} else if (domHuntp) { // No exact scope matches, try only matching the domain
|
||||
domScopep = domHuntp;
|
||||
UINFO(6," MoveIt: SameDomain "<<*domScopep<<endl);
|
||||
} else if (loopHuntp) { // No exact scope or domain matches, only match the loop
|
||||
domScopep = loopHuntp;
|
||||
UINFO(6," MoveIt: SameLoop "<<*domScopep<<endl);
|
||||
} else { // else we're hopefully all done
|
||||
if (curLoop != LOOPID_NOTLOOPED)
|
||||
domScopep->domainp()->v3fatalSrc("Can't find more nodes like "<<*domScopep); // Should be at least a "end loop"
|
||||
break;
|
||||
}
|
||||
// Work on all vertices in this loop/domain/scope
|
||||
OrderMoveVertex* topVertexp = domScopep->readyVertices().begin();
|
||||
UASSERT(topVertexp, "domScope on ready list without any nodes ready under it");
|
||||
m_pomNewFuncp = NULL;
|
||||
while (OrderMoveVertex* vertexp = domScopep->readyVertices().begin()) {
|
||||
processMoveOne(vertexp, domScopep, 1);
|
||||
if (curLoop != processMoveLoopCurrent()) break; // Hit a LoopBegin/end, change loop
|
||||
}
|
||||
}
|
||||
if (!m_pomWaiting.empty()) {
|
||||
OrderMoveVertex* vertexp = m_pomWaiting.begin();
|
||||
vertexp->logicp()->nodep()->v3fatalSrc("Didn't converge; nodes waiting, none ready, perhaps some input activations lost: "<<vertexp<<endl);
|
||||
}
|
||||
#else
|
||||
while (!m_pomReadyDomScope.empty()) {
|
||||
// Start with top node on ready list's domain & scope
|
||||
OrderMoveDomScope* domScopep = m_pomReadyDomScope.begin();
|
||||
OrderMoveVertex* topVertexp = domScopep->readyVertices().begin();
|
||||
OrderMoveVertex* topVertexp = domScopep->readyVertices().begin(); // lintok-begin-on-ref
|
||||
UASSERT(topVertexp, "domScope on ready list without any nodes ready under it");
|
||||
// Work on all scopes ready inside this domain
|
||||
while (domScopep) {
|
||||
UINFO(6," MoveDomain l="<<domScopep->domainp()<<endl);
|
||||
// Process all nodes ready under same domain & scope
|
||||
m_pomNewFuncp = NULL;
|
||||
while (OrderMoveVertex* vertexp = domScopep->readyVertices().begin()) {
|
||||
while (OrderMoveVertex* vertexp = domScopep->readyVertices().begin()) { // lintok-begin-on-ref
|
||||
processMoveOne(vertexp, domScopep, 1);
|
||||
}
|
||||
// Done with scope/domain pair, pick new scope under same domain, or NULL if none left
|
||||
@@ -1504,7 +1214,6 @@ void OrderVisitor::processMove() {
|
||||
}
|
||||
}
|
||||
UASSERT (m_pomWaiting.empty(), "Didn't converge; nodes waiting, none ready, perhaps some input activations lost.");
|
||||
#endif
|
||||
// Cleanup memory
|
||||
processMoveClear();
|
||||
}
|
||||
@@ -1578,36 +1287,7 @@ void OrderVisitor::processMoveOne(OrderMoveVertex* vertexp, OrderMoveDomScope* d
|
||||
AstNode* nodep = lvertexp->nodep();
|
||||
AstNodeModule* modp = scopep->user1p()->castNode()->castNodeModule(); UASSERT(modp,"NULL"); // Stashed by visitor func
|
||||
if (nodep->castUntilStable()) {
|
||||
#ifdef NEW_ORDERING
|
||||
// Beginning of loop.
|
||||
if (OrderLoopBeginVertex* beginp = dynamic_cast<OrderLoopBeginVertex*>(lvertexp)) {
|
||||
m_pomNewFuncp = NULL; // Close out any old function
|
||||
// Create new active record
|
||||
string name = cfuncName(modp, domainp, scopep, nodep);
|
||||
AstActive* callunderp = new AstActive(nodep->fileline(), name, domainp);
|
||||
if (domainp == m_deleteDomainp) {
|
||||
UINFO(6," Delete loop "<<beginp<<endl);
|
||||
pushDeletep(callunderp);
|
||||
} else {
|
||||
processMoveLoopStmt(callunderp);
|
||||
}
|
||||
// Put loop under the activate
|
||||
nodep->unlinkFrBack();
|
||||
callunderp->addStmtsp(nodep);
|
||||
// Remember we're in a loop
|
||||
processMoveLoopPush(beginp);
|
||||
}
|
||||
else if (OrderLoopEndVertex* endp = dynamic_cast<OrderLoopEndVertex*>(lvertexp)) {
|
||||
// Nodep is identical to OrderLoopBeginVertex's, so we don't move it
|
||||
m_pomNewFuncp = NULL; // Close out any old function
|
||||
processMoveLoopPop(endp->beginVertexp());
|
||||
}
|
||||
else {
|
||||
nodep->v3fatalSrc("AstUntilStable node isn't under an OrderLoop{End}Vertex.\n");
|
||||
}
|
||||
#else
|
||||
nodep->v3fatalSrc("Not implemented");
|
||||
#endif
|
||||
}
|
||||
else if (nodep->castSenTree()) {
|
||||
// Just ignore sensitivities, we'll deal with them when we move statements that need them
|
||||
@@ -1708,55 +1388,10 @@ inline void OrderMoveDomScope::movedVertex(OrderVisitor* ovp, OrderMoveVertex* v
|
||||
//######################################################################
|
||||
// Top processing
|
||||
|
||||
#ifdef NEW_ORDERING
|
||||
void OrderVisitor::processLoops(string stepName, V3EdgeFuncP edgeFuncp) {
|
||||
UINFO(2," "<<stepName<<" Loop Detect...\n");
|
||||
m_graph.stronglyConnected(edgeFuncp);
|
||||
m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_strong", true);
|
||||
|
||||
UINFO(2," "<<stepName<<" Insert Loop Begin/Ends...\n");
|
||||
processInsLoop();
|
||||
if (debug()>=4) m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_loops", true);
|
||||
|
||||
// Remove loops from the graph
|
||||
UINFO(2," "<<stepName<<" Acyclic...\n");
|
||||
m_graph.acyclic(edgeFuncp);
|
||||
if (debug()) m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_acyc", true);
|
||||
|
||||
// For any broken edges, add to change detect and remove edge
|
||||
UINFO(2," "<<stepName<<" ProcessBrokeLoop...\n");
|
||||
processBrokeLoop();
|
||||
if (debug()>=4) m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_broke", true);
|
||||
|
||||
m_graph.makeEdgesNonCutable(edgeFuncp);
|
||||
m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_done", true);
|
||||
}
|
||||
#endif
|
||||
|
||||
void OrderVisitor::process() {
|
||||
// Dump data
|
||||
m_graph.dumpDotFilePrefixed("orderg_pre");
|
||||
|
||||
#ifdef NEW_ORDERING
|
||||
// Ignoring POST assignments and clocked statements, detect strongly connected components
|
||||
// Split components into subgraphs
|
||||
|
||||
// Detect, loop begin/end, and acyc combo loops
|
||||
UINFO(2," Combo loop elimination...\n");
|
||||
processLoops("combo", &OrderEdge::followComboConnected);
|
||||
|
||||
// Detect, loop begin/end, and acyc post assigns
|
||||
UINFO(2," Sequential loop elimination...\n");
|
||||
processLoops("sequent", &OrderEdge::followSequentConnected);
|
||||
|
||||
// Detect and acyc any PRE assigns
|
||||
// As breaking these does not cause any problems, we don't need to loop begin/end, etc.
|
||||
UINFO(2," Pre Loop Detect & Acyc...\n");
|
||||
m_graph.stronglyConnected(&V3GraphEdge::followAlwaysTrue);
|
||||
if (debug()>=4) m_graph.dumpDotFilePrefixed("orderg_preasn_strong", true);
|
||||
m_graph.acyclic(&V3GraphEdge::followAlwaysTrue);
|
||||
m_graph.dumpDotFilePrefixed("orderg_preasn_done", true);
|
||||
#else
|
||||
// Break cycles. Each strongly connected subgraph (including cutable
|
||||
// edges) will have its own color, and corresponds to a loop in the
|
||||
// original graph. However the new graph will be acyclic (the removed
|
||||
@@ -1764,7 +1399,6 @@ void OrderVisitor::process() {
|
||||
UINFO(2," Acyclic & Order...\n");
|
||||
m_graph.acyclic(&V3GraphEdge::followAlwaysTrue);
|
||||
m_graph.dumpDotFilePrefixed("orderg_acyc");
|
||||
#endif
|
||||
|
||||
// Assign ranks so we know what to follow
|
||||
// Then, sort vertices and edges by that ordering
|
||||
@@ -1777,10 +1411,8 @@ void OrderVisitor::process() {
|
||||
UINFO(2," Process Clocks...\n");
|
||||
processInputs(); // must be before processCircular
|
||||
|
||||
#ifndef NEW_ORDERING
|
||||
UINFO(2," Process Circulars...\n");
|
||||
processCircular(); // must be before processDomains
|
||||
#endif
|
||||
|
||||
// Assign logic verticesto new domains
|
||||
UINFO(2," Domains...\n");
|
||||
|
||||
+2
-1
@@ -228,7 +228,8 @@ class OrderVarVertex : public OrderEitherVertex {
|
||||
protected:
|
||||
OrderVarVertex(V3Graph* graphp, const OrderVarVertex& old)
|
||||
: OrderEitherVertex(graphp, old)
|
||||
, m_varScp(old.m_varScp), m_pilNewVertexp(old.m_pilNewVertexp), m_isClock(old.m_isClock) {}
|
||||
, m_varScp(old.m_varScp), m_pilNewVertexp(old.m_pilNewVertexp), m_isClock(old.m_isClock)
|
||||
, m_isDelayed(old.m_isDelayed) {}
|
||||
public:
|
||||
OrderVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: OrderEitherVertex(graphp, scopep, NULL), m_varScp(varScp)
|
||||
|
||||
+11
-2
@@ -210,7 +210,8 @@ private:
|
||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||
if (nodep->dead()) {
|
||||
UINFO(4," MOD-dead. "<<nodep<<endl); // Marked by LinkDot
|
||||
} else if (nodep->level() <= 2) { // Haven't added top yet, so level 2 is the top
|
||||
} else if (nodep->level() <= 2 // Haven't added top yet, so level 2 is the top
|
||||
|| nodep->castPackage()) { // Likewise haven't done wrapTopPackages yet
|
||||
// Add request to END of modules left to process
|
||||
m_todoModps.insert(make_pair(nodep->level(),nodep));
|
||||
visitModules();
|
||||
@@ -230,8 +231,16 @@ private:
|
||||
if (!nodep->user5SetOnce()) { // Process once
|
||||
nodep->iterateChildren(*this);
|
||||
if (nodep->isParam()) {
|
||||
if (!nodep->hasSimpleInit()) { nodep->v3fatalSrc("Parameter without initial value"); }
|
||||
if (!nodep->valuep()) { nodep->v3fatalSrc("Parameter without initial value"); }
|
||||
V3Const::constifyParamsEdit(nodep); // The variable, not just the var->init()
|
||||
if (!nodep->valuep()->castConst()) { // Complex init, like an array
|
||||
// Make a new INITIAL to set the value.
|
||||
// This allows the normal array/struct handling code to properly initialize the parameter
|
||||
nodep->addNext(new AstInitial(nodep->fileline(),
|
||||
new AstAssign(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep, true),
|
||||
nodep->valuep()->cloneTree(true))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,7 @@ void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool i
|
||||
string vppfilename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_"+modname+".vpp";
|
||||
ofstream* ofp = NULL;
|
||||
ostream* osp;
|
||||
bool noblanks = v3Global.opt.preprocOnly() && v3Global.opt.preprocNoLine();
|
||||
if (v3Global.opt.preprocOnly()) {
|
||||
osp = &cout;
|
||||
} else {
|
||||
@@ -133,6 +134,13 @@ void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool i
|
||||
return;
|
||||
} else {
|
||||
for (deque<string>::iterator it = m_ppBuffers.begin(); it!=m_ppBuffers.end(); ++it) {
|
||||
if (noblanks) {
|
||||
bool blank = true;
|
||||
for (string::iterator its = it->begin(); its != it->end(); ++its) {
|
||||
if (!isspace(*its) && *its!='\n') { blank=false; break; }
|
||||
}
|
||||
if (blank) continue;
|
||||
}
|
||||
*osp << *it;
|
||||
}
|
||||
if (ofp) {
|
||||
|
||||
@@ -105,6 +105,7 @@ class V3ParseImp {
|
||||
int m_inBeginKwd; // Inside a `begin_keywords
|
||||
int m_lastVerilogState; // Last LEX state in `begin_keywords
|
||||
|
||||
int m_prevLexToken; // previous parsed token (for lexer)
|
||||
bool m_ahead; // aheadToken is valid
|
||||
int m_aheadToken; // Token we read ahead
|
||||
V3ParseBisonYYSType m_aheadVal; // aheadToken's value
|
||||
@@ -193,6 +194,7 @@ public:
|
||||
void statePushVlg(); // Parser -> lexer communication
|
||||
void statePop(); // Parser -> lexer communication
|
||||
static int stateVerilogRecent(); // Parser -> lexer communication
|
||||
int prevLexToken() { return m_prevLexToken; } // Parser -> lexer communication
|
||||
size_t flexPpInputToLex(char* buf, size_t max_size) { return ppInputToLex(buf,max_size); }
|
||||
|
||||
//==== Symbol tables
|
||||
@@ -208,11 +210,13 @@ public:
|
||||
m_inLibrary = false;
|
||||
m_inBeginKwd = 0;
|
||||
m_lastVerilogState = stateVerilogRecent();
|
||||
m_prevLexToken = 0;
|
||||
m_ahead = false;
|
||||
m_aheadToken = 0;
|
||||
}
|
||||
~V3ParseImp();
|
||||
void parserClear();
|
||||
void unputString(const char* textp, size_t length);
|
||||
|
||||
// METHODS
|
||||
// Preprocess and read the Verilog file specified into the netlist database
|
||||
@@ -223,6 +227,7 @@ public:
|
||||
|
||||
private:
|
||||
void lexFile(const string& modname);
|
||||
int yylexReadTok();
|
||||
int lexToken(); // Internal; called from lexToBison
|
||||
};
|
||||
|
||||
|
||||
+16
-1
@@ -57,11 +57,26 @@ public:
|
||||
void statePop() {
|
||||
yy_pop_state();
|
||||
}
|
||||
void unputString(const char* textp, size_t length) {
|
||||
// Add characters to input stream in back-to-front order
|
||||
const char* cp = textp;
|
||||
for (cp += length - 1; length--; cp--) {
|
||||
unput(*cp);
|
||||
}
|
||||
}
|
||||
};
|
||||
void V3ParseImp::stateExitPsl() { parsep()->m_lexerp->stateExitPsl(); }
|
||||
void V3ParseImp::statePushVlg() { parsep()->m_lexerp->stateExitPsl(); }
|
||||
void V3ParseImp::statePop() { parsep()->m_lexerp->statePop(); }
|
||||
int V3ParseImp::yylexThis() { return parsep()->m_lexerp->yylex(); }
|
||||
|
||||
void V3ParseImp::unputString(const char* textp, size_t length) { parsep()->m_lexerp->unputString(textp, length); }
|
||||
|
||||
int V3ParseImp::yylexReadTok() {
|
||||
// Call yylex() remembering last non-whitespace token
|
||||
int token = parsep()->m_lexerp->yylex();
|
||||
m_prevLexToken = token; // Save so can find '#' to parse following number
|
||||
return token;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Read class functions
|
||||
|
||||
@@ -169,6 +169,7 @@ class V3PreLex {
|
||||
int m_pslParenLevel;// PSL Parenthesis (){} counting, so we can find final ;
|
||||
bool m_pslMoreNeeded;// Next // comment is really psl
|
||||
bool m_defCmtSlash; // /*...*/ comment in define had \ ending
|
||||
bool m_defQuote; // Definition value inside quote
|
||||
string m_defValue; // Definition value being built.
|
||||
int m_enterExit; // For VL_LINE, the enter/exit level
|
||||
|
||||
@@ -181,6 +182,7 @@ class V3PreLex {
|
||||
m_pedantic = false;
|
||||
m_formalLevel = 0;
|
||||
m_parenLevel = 0;
|
||||
m_defQuote = false;
|
||||
m_defCmtSlash = false;
|
||||
m_tokFilelinep = filelinep;
|
||||
m_enterExit = 0;
|
||||
|
||||
+5
-4
@@ -107,7 +107,7 @@ psl [p]sl
|
||||
|
||||
/* Optional directives we recognize */
|
||||
<INITIAL>"`__FILE__" { static string rtnfile;
|
||||
rtnfile = '"'; rtnfile += LEXP->curFilelinep()->filename().c_str();
|
||||
rtnfile = '"'; rtnfile += LEXP->curFilelinep()->filename();
|
||||
rtnfile += '"'; yytext=(char*)rtnfile.c_str(); yyleng = rtnfile.length();
|
||||
return (VP_STRING); }
|
||||
<INITIAL>"`__LINE__" { static char buf[10];
|
||||
@@ -125,7 +125,7 @@ psl [p]sl
|
||||
<STRMODE>{backslash}{crnl} { linenoInc(); yymore(); }
|
||||
<STRMODE>{backslash}. { yymore(); }
|
||||
<STRMODE>{quote} { yy_pop_state();
|
||||
if (LEXP->m_parenLevel || LEXP->m_formalLevel) { appendDefValue(yytext,yyleng); yyleng=0; }
|
||||
if (LEXP->m_parenLevel || LEXP->m_defQuote) { LEXP->m_defQuote=false; appendDefValue(yytext,yyleng); yyleng=0; }
|
||||
else return (VP_STRING); }
|
||||
|
||||
/* Stringification */
|
||||
@@ -162,7 +162,7 @@ psl [p]sl
|
||||
<DEFFORM><<EOF>> { linenoInc(); yy_pop_state(); yyerrorf("Unterminated ( in define formal arguments."); yyleng=0; return VP_DEFFORM; }
|
||||
<DEFFORM>{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */
|
||||
<DEFFORM>[\\]{crnl} { linenoInc(); appendDefValue((char*)"\\\n",2); } /* Include return so can maintain output line count */
|
||||
<DEFFORM>{quote} { yy_push_state(STRMODE); yymore(); } /* Legal only in default values */
|
||||
<DEFFORM>{quote} { LEXP->m_defQuote=true; yy_push_state(STRMODE); yymore(); } /* Legal only in default values */
|
||||
<DEFFORM>"`\\`\"" { appendDefValue(yytext,yyleng); } /* Maybe illegal, otherwise in default value */
|
||||
<DEFFORM>{tickquote} { appendDefValue(yytext,yyleng); } /* Maybe illegal, otherwise in default value */
|
||||
<DEFFORM>[{\[] { LEXP->m_formalLevel++; appendDefValue(yytext,yyleng); }
|
||||
@@ -179,7 +179,8 @@ psl [p]sl
|
||||
<DEFVAL><<EOF>> { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); } /* Technically illegal, but people complained */
|
||||
<DEFVAL>{crnl} { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); }
|
||||
<DEFVAL>[\\]{crnl} { linenoInc(); appendDefValue((char*)"\\\n",2); } /* Return, AND \ is part of define value */
|
||||
<DEFVAL>[^\/\*\n\r\\]+ |
|
||||
<DEFVAL>{quote} { LEXP->m_defQuote=true; yy_push_state(STRMODE); yymore(); }
|
||||
<DEFVAL>[^\/\*\n\r\\\"]+ |
|
||||
<DEFVAL>[\\][^\n\r] |
|
||||
<DEFVAL>. { appendDefValue(yytext,yyleng); }
|
||||
|
||||
|
||||
+15
-9
@@ -566,7 +566,8 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) {
|
||||
bool quote = false;
|
||||
bool haveDefault = false;
|
||||
// Note there's a leading ( and trailing ), so parens==1 is the base parsing level
|
||||
const char* cp=refp->params().c_str();
|
||||
string params = refp->params(); // Must keep in scope
|
||||
const char* cp=params.c_str();
|
||||
if (*cp == '(') cp++;
|
||||
for (; *cp; cp++) {
|
||||
//UINFO(4," Parse Paren="<<paren<<" Arg="<<numArgs<<" token='"<<token<<"' Parse="<<cp<<endl);
|
||||
@@ -1367,9 +1368,11 @@ int V3PreProcImp::getFinalToken(string& buf) {
|
||||
}
|
||||
int tok = m_finToken;
|
||||
buf = m_finBuf;
|
||||
if (0 && debug()>=5) fprintf (stderr,"%d: FIN: %-10s: %s\n",
|
||||
m_lexp->m_tokFilelinep->lineno(),
|
||||
tokenName(tok), V3PreLex::cleanDbgStrg(buf).c_str());
|
||||
if (0 && debug()>=5) {
|
||||
string bufcln = V3PreLex::cleanDbgStrg(buf);
|
||||
fprintf (stderr,"%d: FIN: %-10s: %s\n",
|
||||
m_lexp->m_tokFilelinep->lineno(), tokenName(tok), bufcln.c_str());
|
||||
}
|
||||
// Track `line
|
||||
const char* bufp = buf.c_str();
|
||||
while (*bufp == '\n') bufp++;
|
||||
@@ -1400,7 +1403,7 @@ int V3PreProcImp::getFinalToken(string& buf) {
|
||||
}
|
||||
}
|
||||
// Track newlines in prep for next token
|
||||
for (const char* cp = buf.c_str(); *cp; cp++) {
|
||||
for (string::iterator cp=buf.begin(); cp!=buf.end(); ++cp) {
|
||||
if (*cp == '\n') {
|
||||
m_finAtBol = true;
|
||||
m_finFilelinep->linenoIncInPlace(); // Increment in place to avoid new/delete calls. It's private data.
|
||||
@@ -1422,8 +1425,9 @@ string V3PreProcImp::getline() {
|
||||
string buf;
|
||||
int tok = getFinalToken(buf/*ref*/);
|
||||
if (debug()>=5) {
|
||||
string bufcln = V3PreLex::cleanDbgStrg(buf);
|
||||
fprintf (stderr,"%d: GETFETC: %-10s: %s\n",
|
||||
m_lexp->m_tokFilelinep->lineno(), tokenName(tok), V3PreLex::cleanDbgStrg(buf).c_str());
|
||||
m_lexp->m_tokFilelinep->lineno(), tokenName(tok), bufcln.c_str());
|
||||
}
|
||||
if (tok==VP_EOF) {
|
||||
// Add a final newline, if the user forgot the final \n.
|
||||
@@ -1444,8 +1448,10 @@ string V3PreProcImp::getline() {
|
||||
int len = rtnp-m_lineChars.c_str()+1;
|
||||
string theLine(m_lineChars, 0, len);
|
||||
m_lineChars = m_lineChars.erase(0,len); // Remove returned characters
|
||||
if (debug()>=4) fprintf (stderr,"%d: GETLINE: %s\n",
|
||||
m_lexp->m_tokFilelinep->lineno(),
|
||||
V3PreLex::cleanDbgStrg(theLine).c_str());
|
||||
if (debug()>=4) {
|
||||
string lncln = V3PreLex::cleanDbgStrg(theLine);
|
||||
fprintf (stderr,"%d: GETLINE: %s\n",
|
||||
m_lexp->m_tokFilelinep->lineno(), lncln.c_str());
|
||||
}
|
||||
return theLine;
|
||||
}
|
||||
|
||||
+3
-1
@@ -71,7 +71,9 @@ public:
|
||||
// These options control how the parsing proceeds
|
||||
static int keepComments() { return 2; } // Return comments, 0=no, 1=yes, 2=callback
|
||||
static bool keepWhitespace() { return false; }
|
||||
static bool lineDirectives() { return true; } // Insert `line directives
|
||||
static bool lineDirectives() { // Insert `line directives
|
||||
return !(v3Global.opt.preprocOnly() && v3Global.opt.preprocNoLine());
|
||||
}
|
||||
static bool pedantic() { return false; } // Obey standard; Don't substitute `error
|
||||
static bool optPsl();
|
||||
|
||||
|
||||
+70
-5
@@ -40,6 +40,60 @@
|
||||
#include "V3Premit.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
|
||||
//######################################################################
|
||||
// Structure for global state
|
||||
|
||||
class PremitAssignVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstVar::user4() // bool; occurs on LHS of current assignment
|
||||
AstUser4InUse m_inuser4;
|
||||
|
||||
// STATE
|
||||
bool m_noopt; // Disable optimization of variables in this block
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
//AstNode::user4ClearTree(); // Implied by AstUser4InUse
|
||||
// LHS first as fewer varrefs
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
// Now find vars marked as lhs
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
// it's LHS var is used so need a deep temporary
|
||||
if (nodep->lvalue()) {
|
||||
nodep->varp()->user4(true);
|
||||
} else {
|
||||
if (nodep->varp()->user4()) {
|
||||
if (!m_noopt) UINFO(4, "Block has LHS+RHS var: "<<nodep<<endl);
|
||||
m_noopt = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
PremitAssignVisitor(AstNodeAssign* nodep) {
|
||||
UINFO(4," PremitAssignVisitor on "<<nodep<<endl);
|
||||
m_noopt = false;
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~PremitAssignVisitor() {}
|
||||
bool noOpt() const { return m_noopt; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Premit state, as a visitor of each AstNode
|
||||
|
||||
@@ -49,6 +103,7 @@ private:
|
||||
// AstNodeMath::user() -> bool. True if iterated already
|
||||
// AstShiftL::user2() -> bool. True if converted to conditional
|
||||
// AstShiftR::user2() -> bool. True if converted to conditional
|
||||
// *::user4() -> See PremitAssignVisitor
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
|
||||
@@ -115,11 +170,13 @@ private:
|
||||
m_inWhilep->addPrecondsp(newp);
|
||||
} else if (m_inTracep) {
|
||||
m_inTracep->addPrecondsp(newp);
|
||||
} else {
|
||||
} else if (m_stmtp) {
|
||||
AstNRelinker linker;
|
||||
m_stmtp->unlinkFrBack(&linker);
|
||||
newp->addNext(m_stmtp);
|
||||
linker.relink(newp);
|
||||
} else {
|
||||
newp->v3fatalSrc("No statement insertion point.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +231,14 @@ private:
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
startStatement(nodep);
|
||||
{
|
||||
bool noopt = PremitAssignVisitor(nodep).noOpt();
|
||||
if (noopt && !nodep->user1()) {
|
||||
// Need to do this even if not wide, as e.g. a select may be on a wide operator
|
||||
UINFO(4,"Deep temp for LHS/RHS\n");
|
||||
createDeepTemp(nodep->rhsp());
|
||||
}
|
||||
}
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
m_assignLhs = true;
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
@@ -225,9 +290,9 @@ private:
|
||||
constwidthp->dtypeFrom (nodep->rhsp()); // unsigned
|
||||
AstCond* newp =
|
||||
new AstCond (nodep->fileline(),
|
||||
new AstLte (nodep->fileline(),
|
||||
nodep->rhsp()->cloneTree(false),
|
||||
constwidthp),
|
||||
new AstGte (nodep->fileline(),
|
||||
constwidthp,
|
||||
nodep->rhsp()->cloneTree(false)),
|
||||
nodep,
|
||||
constzerop);
|
||||
replaceHandle.relink(newp);
|
||||
@@ -287,7 +352,7 @@ private:
|
||||
while (searchp && searchp->castComment()) searchp = searchp->nextp();
|
||||
if (searchp
|
||||
&& searchp->castDisplay()
|
||||
&& nodep->filep()->sameTree(searchp->castDisplay()->filep())) {
|
||||
&& nodep->filep()->sameGateTree(searchp->castDisplay()->filep())) {
|
||||
// There's another display next; we can just wait to flush
|
||||
} else {
|
||||
UINFO(4,"Autoflush "<<nodep<<endl);
|
||||
|
||||
+43
-13
@@ -46,10 +46,13 @@ private:
|
||||
// NODE STATE
|
||||
// AstVar::user1p -> AstVarScope replacement for this variable
|
||||
// AstTask::user2p -> AstTask*. Replacement task
|
||||
// AstVar::user3p -> AstVarScope for packages
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
|
||||
// TYPES
|
||||
typedef map<AstPackage*, AstScope*> PackageScopeMap;
|
||||
typedef map<pair<AstVar*, AstScope*>, AstVarScope*> VarScopeMap;
|
||||
typedef set<pair<AstVarRef*, AstScope*> > VarRefScopeSet;
|
||||
|
||||
// STATE, inside processing a single module
|
||||
AstNodeModule* m_modp; // Current module
|
||||
@@ -57,6 +60,10 @@ private:
|
||||
// STATE, for passing down one level of hierarchy (may need save/restore)
|
||||
AstCell* m_aboveCellp; // Cell that instantiates this module
|
||||
AstScope* m_aboveScopep; // Scope that instantiates this scope
|
||||
|
||||
PackageScopeMap m_packageScopes; // Scopes for each package
|
||||
VarScopeMap m_varScopes; // Varscopes created for each scope and var
|
||||
VarRefScopeSet m_varRefScopes; // Varrefs-in-scopes needing fixup when donw
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
@@ -65,6 +72,23 @@ private:
|
||||
return level;
|
||||
}
|
||||
|
||||
void cleanupVarRefs() {
|
||||
for (VarRefScopeSet::iterator it = m_varRefScopes.begin();
|
||||
it!=m_varRefScopes.end(); ++it) {
|
||||
AstVarRef* nodep = it->first;
|
||||
AstScope* scopep = it->second;
|
||||
if (nodep->packagep()) {
|
||||
PackageScopeMap::iterator it2 = m_packageScopes.find(nodep->packagep());
|
||||
if (it2==m_packageScopes.end()) nodep->v3fatalSrc("Can't locate package scope");
|
||||
scopep = it2->second;
|
||||
}
|
||||
VarScopeMap::iterator it3 = m_varScopes.find(make_pair(nodep->varp(), scopep));
|
||||
if (it3==m_varScopes.end()) nodep->v3fatalSrc("Can't locate varref scope");
|
||||
AstVarScope* varscp = it3->second;
|
||||
nodep->varScopep(varscp);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep, AstNUser*) {
|
||||
AstNodeModule* modp = nodep->topModulep();
|
||||
@@ -73,6 +97,7 @@ private:
|
||||
m_aboveCellp = NULL;
|
||||
m_aboveScopep = NULL;
|
||||
modp->accept(*this);
|
||||
cleanupVarRefs();
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||
// Create required blocks and add to module
|
||||
@@ -85,6 +110,7 @@ private:
|
||||
|
||||
m_scopep = new AstScope((m_aboveCellp?(AstNode*)m_aboveCellp:(AstNode*)nodep)->fileline(),
|
||||
nodep, scopename, m_aboveScopep, m_aboveCellp);
|
||||
if (nodep->castPackage()) m_packageScopes.insert(make_pair(nodep->castPackage(), m_scopep));
|
||||
|
||||
// Now for each child cell, iterate the module this cell points to
|
||||
for (AstNode* cellnextp = nodep->stmtsp(); cellnextp; cellnextp=cellnextp->nextp()) {
|
||||
@@ -211,12 +237,14 @@ private:
|
||||
}
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
// Make new scope variable
|
||||
if (m_modp->castPackage()
|
||||
? !nodep->user3p() : !nodep->user1p()) {
|
||||
// This is called cross-module by AstVar, so we cannot trust any m_ variables
|
||||
if (!nodep->user1p()) {
|
||||
AstVarScope* varscp = new AstVarScope(nodep->fileline(), m_scopep, nodep);
|
||||
UINFO(6," New scope "<<varscp<<endl);
|
||||
if (m_aboveCellp && !m_aboveCellp->isTrace()) varscp->trace(false);
|
||||
nodep->user1p(varscp);
|
||||
if (m_modp->castPackage()) nodep->user3p(varscp);
|
||||
if (!m_scopep) nodep->v3fatalSrc("No scope for var");
|
||||
m_varScopes.insert(make_pair(make_pair(nodep, m_scopep), varscp));
|
||||
m_scopep->addVarp(varscp);
|
||||
}
|
||||
}
|
||||
@@ -227,14 +255,12 @@ private:
|
||||
if (nodep->varp()->isIfaceRef()) {
|
||||
nodep->varScopep(NULL);
|
||||
} else {
|
||||
nodep->varp()->accept(*this);
|
||||
AstVarScope* varscp = nodep->packagep()
|
||||
? (AstVarScope*)nodep->varp()->user3p()
|
||||
: (AstVarScope*)nodep->varp()->user1p();
|
||||
if (!varscp) nodep->v3fatalSrc("Can't locate varref scope");
|
||||
nodep->varScopep(varscp);
|
||||
}
|
||||
}
|
||||
// We may have not made the variable yet, and we can't make it now as
|
||||
// the var's referenced package etc might not be created yet.
|
||||
// So push to a list and post-correct
|
||||
m_varRefScopes.insert(make_pair(nodep, m_scopep));
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScopeName* nodep, AstNUser*) {
|
||||
// If there's a %m in the display text, we add a special node that will contain the name()
|
||||
string prefix = (string)("__DOT__")+m_scopep->name();
|
||||
@@ -245,6 +271,10 @@ private:
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeAttrp(new AstText(nodep->fileline(), prefix));
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
afterp = nodep->scopeEntrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeEntrp(new AstText(nodep->fileline(), prefix));
|
||||
if (afterp) nodep->scopeEntrp(afterp);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstScope* nodep, AstNUser*) {
|
||||
|
||||
+41
-31
@@ -70,7 +70,7 @@ class SliceCloneVisitor : public AstNVisitor {
|
||||
// VISITORS
|
||||
virtual void visit(AstArraySel* nodep, AstNUser*) {
|
||||
if (!nodep->backp()->castArraySel()) {
|
||||
// This is the top of an ArraySel, setup
|
||||
// This is the top of an ArraySel, setup for iteration
|
||||
m_refp = nodep->user1p()->castNode()->castVarRef();
|
||||
m_vecIdx += 1;
|
||||
if (m_vecIdx == (int)m_selBits.size()) {
|
||||
@@ -78,6 +78,7 @@ class SliceCloneVisitor : public AstNVisitor {
|
||||
AstVar* varp = m_refp->varp();
|
||||
pair<uint32_t,uint32_t> arrDim = varp->dtypep()->dimensions(false);
|
||||
uint32_t dimensions = arrDim.second;
|
||||
// for 3-dimensions we want m_selBits[m_vecIdx]=[0,0,0]
|
||||
for (uint32_t i = 0; i < dimensions; ++i) {
|
||||
m_selBits[m_vecIdx].push_back(0);
|
||||
}
|
||||
@@ -165,19 +166,15 @@ class SliceCloneVisitor : public AstNVisitor {
|
||||
nodep->addNextHere(lhsp);
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep = NULL;
|
||||
}
|
||||
|
||||
virtual void visit(AstRedOr* nodep, AstNUser*) {
|
||||
cloneUniop(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstRedAnd* nodep, AstNUser*) {
|
||||
cloneUniop(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstRedXor* nodep, AstNUser*) {
|
||||
cloneUniop(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstRedXnor* nodep, AstNUser*) {
|
||||
cloneUniop(nodep);
|
||||
}
|
||||
@@ -188,8 +185,8 @@ class SliceCloneVisitor : public AstNVisitor {
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
SliceCloneVisitor(AstNode* assignp) {
|
||||
assignp->accept(*this);
|
||||
SliceCloneVisitor(AstNode* nodep) {
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~SliceCloneVisitor() {}
|
||||
};
|
||||
@@ -223,8 +220,8 @@ class SliceVisitor : public AstNVisitor {
|
||||
return level;
|
||||
}
|
||||
|
||||
// Find out how many explicit dimensions are in a given ArraySel.
|
||||
unsigned explicitDimensions(AstArraySel* nodep) {
|
||||
// Find out how many explicit dimensions are in a given ArraySel.
|
||||
unsigned dim = 0;
|
||||
AstNode* fromp = nodep;
|
||||
AstArraySel* selp;
|
||||
@@ -243,10 +240,23 @@ class SliceVisitor : public AstNVisitor {
|
||||
return dim;
|
||||
}
|
||||
|
||||
int countClones(AstArraySel* nodep) {
|
||||
// Count how many clones we need to make from this ArraySel
|
||||
int clones = 1;
|
||||
AstNode* fromp = nodep;
|
||||
AstArraySel* selp;
|
||||
do {
|
||||
selp = fromp->castArraySel();
|
||||
fromp = (selp) ? selp->fromp() : NULL;
|
||||
if (fromp && selp) clones *= selp->length();
|
||||
} while (fromp && selp);
|
||||
return clones;
|
||||
}
|
||||
|
||||
AstArraySel* insertImplicit(AstNode* nodep, unsigned start, unsigned count) {
|
||||
// Insert any implicit slices as explicit slices (ArraySel nodes).
|
||||
// Return a new pointer to replace nodep() in the ArraySel.
|
||||
UINFO(9," insertImplicit "<<nodep<<endl);
|
||||
UINFO(9," insertImplicit (start="<<start<<",c="<<count<<") "<<nodep<<endl);
|
||||
AstVarRef* refp = nodep->user1p()->castNode()->castVarRef();
|
||||
if (!refp) nodep->v3fatalSrc("No VarRef in user1 of node "<<nodep);
|
||||
AstVar* varp = refp->varp();
|
||||
@@ -269,28 +279,15 @@ class SliceVisitor : public AstNVisitor {
|
||||
newp->user1p(refp);
|
||||
newp->start(lsb);
|
||||
newp->length(msb - lsb + 1);
|
||||
topp = newp->castNode();
|
||||
topp = newp;
|
||||
}
|
||||
return topp->castArraySel();
|
||||
}
|
||||
|
||||
int countClones(AstArraySel* nodep) {
|
||||
// Count how many clones we need to make from this ArraySel
|
||||
int clones = 1;
|
||||
AstNode* fromp = nodep;
|
||||
AstArraySel* selp;
|
||||
do {
|
||||
selp = fromp->castArraySel();
|
||||
fromp = (selp) ? selp->fromp() : NULL;
|
||||
if (fromp && selp) clones *= selp->length();
|
||||
} while (fromp && selp);
|
||||
return clones;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
// The LHS/RHS of an Assign may be to a Var that is an array. In this
|
||||
// case we need to create a slice accross the entire Var
|
||||
// case we need to create a slice across the entire Var
|
||||
if (m_assignp && !nodep->backp()->castArraySel()) {
|
||||
pair<uint32_t,uint32_t> arrDim = nodep->varp()->dtypep()->dimensions(false);
|
||||
uint32_t dimensions = arrDim.second; // unpacked only
|
||||
@@ -382,8 +379,8 @@ class SliceVisitor : public AstNVisitor {
|
||||
refp = findVarRefRecurse(nodep->op3p());
|
||||
if (refp) return refp;
|
||||
}
|
||||
if (nodep->op3p()) {
|
||||
refp = findVarRefRecurse(nodep->op3p());
|
||||
if (nodep->op4p()) {
|
||||
refp = findVarRefRecurse(nodep->op4p());
|
||||
if (refp) return refp;
|
||||
}
|
||||
if (nodep->nextp()) {
|
||||
@@ -411,6 +408,23 @@ class SliceVisitor : public AstNVisitor {
|
||||
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
if (!nodep->user1()) {
|
||||
// Cleanup initArrays
|
||||
if (AstInitArray* initp = nodep->rhsp()->castInitArray()) {
|
||||
//if (debug()>=9) nodep->dumpTree(cout, "-InitArrayIn: ");
|
||||
AstNode* newp = NULL;
|
||||
int index = 0;
|
||||
while (AstNode* subp=initp->initsp()) {
|
||||
AstNode* lhsp = new AstArraySel(nodep->fileline(),
|
||||
nodep->lhsp()->cloneTree(false),
|
||||
index++);
|
||||
// cppcheck-suppress nullPointer
|
||||
newp = newp->addNext(nodep->cloneType(lhsp, subp->unlinkFrBack()));
|
||||
}
|
||||
//if (debug()>=9) newp->dumpTreeAndNext(cout, "-InitArrayOut: ");
|
||||
nodep->replaceWith(newp);
|
||||
pushDeletep(nodep); nodep=NULL;
|
||||
return; // WIll iterate in a moment
|
||||
}
|
||||
// Hasn't been searched for implicit slices yet
|
||||
findImplicit(nodep);
|
||||
}
|
||||
@@ -424,7 +438,7 @@ class SliceVisitor : public AstNVisitor {
|
||||
dim = explicitDimensions(selp);
|
||||
}
|
||||
if (dim == 0 && !nodep->lhsp()->castVarRef()) {
|
||||
// No ArraySel or VarRef, not something we can expand
|
||||
// No ArraySel nor VarRef, not something we can expand
|
||||
nodep->iterateChildren(*this);
|
||||
} else {
|
||||
AstVarRef* refp = findVarRefRecurse(nodep->lhsp());
|
||||
@@ -445,25 +459,21 @@ class SliceVisitor : public AstNVisitor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstRedOr* nodep, AstNUser*) {
|
||||
if (!nodep->user1()) {
|
||||
expandUniOp(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstRedAnd* nodep, AstNUser*) {
|
||||
if (!nodep->user1()) {
|
||||
expandUniOp(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstRedXor* nodep, AstNUser*) {
|
||||
if (!nodep->user1()) {
|
||||
expandUniOp(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstRedXnor* nodep, AstNUser*) {
|
||||
if (!nodep->user1()) {
|
||||
expandUniOp(nodep);
|
||||
|
||||
@@ -233,7 +233,6 @@ private:
|
||||
SplitPliVertex* m_pliVertexp; // Element specifying PLI ordering
|
||||
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
||||
bool m_inDly; // Inside ASSIGNDLY
|
||||
uint32_t m_stepNum; // Step number we need to ignore a edge in
|
||||
V3Double0 m_statSplits; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
|
||||
+16
-13
@@ -31,6 +31,9 @@
|
||||
#include "V3Ast.h"
|
||||
#include "V3File.h"
|
||||
|
||||
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
|
||||
#include "V3AstConstOnly.h"
|
||||
|
||||
//######################################################################
|
||||
// Stats class functions
|
||||
|
||||
@@ -74,14 +77,14 @@ private:
|
||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||
allNodes(nodep);
|
||||
if (!m_fast) {
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
} else {
|
||||
for (AstNode* searchp = nodep->stmtsp(); searchp; searchp=searchp->nextp()) {
|
||||
if (AstCFunc* funcp = searchp->castCFunc()) {
|
||||
if (funcp->name() == "_eval") {
|
||||
m_instrs=0;
|
||||
m_counting = true;
|
||||
funcp->iterateChildren(*this);
|
||||
funcp->iterateChildrenConst(*this);
|
||||
m_counting = false;
|
||||
}
|
||||
}
|
||||
@@ -90,7 +93,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
allNodes(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
if (m_counting && nodep->dtypep()) {
|
||||
if (nodep->isUsedClock()) ++m_statVarClock;
|
||||
if (nodep->dtypeSkipRefp()->castUnpackArrayDType()) ++m_statVarArray;
|
||||
@@ -103,7 +106,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep, AstNUser*) {
|
||||
allNodes(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
if (m_counting) {
|
||||
if (nodep->varp()->dtypeSkipRefp()->castBasicDType()) {
|
||||
m_statVarScpBytes += nodep->varp()->dtypeSkipRefp()->widthTotalBytes();
|
||||
@@ -114,13 +117,13 @@ private:
|
||||
UINFO(4," IF "<<nodep<<endl);
|
||||
allNodes(nodep);
|
||||
// Condition is part of PREVIOUS block
|
||||
nodep->condp()->iterateAndNext(*this);
|
||||
nodep->condp()->iterateAndNextConst(*this);
|
||||
// Track prediction
|
||||
if (m_counting) {
|
||||
++m_statPred[nodep->branchPred()];
|
||||
}
|
||||
if (!m_fast) {
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
} else {
|
||||
// See which path we want to take
|
||||
bool takeElse = false;
|
||||
@@ -135,11 +138,11 @@ private:
|
||||
m_counting = false;
|
||||
// Check if
|
||||
m_instrs = 0;
|
||||
nodep->ifsp()->iterateAndNext(*this);
|
||||
nodep->ifsp()->iterateAndNextConst(*this);
|
||||
double instrIf = m_instrs;
|
||||
// Check else
|
||||
m_instrs = 0;
|
||||
nodep->elsesp()->iterateAndNext(*this);
|
||||
nodep->elsesp()->iterateAndNextConst(*this);
|
||||
double instrElse = m_instrs;
|
||||
// Max of if or else condition
|
||||
takeElse = (instrElse > instrIf);
|
||||
@@ -150,9 +153,9 @@ private:
|
||||
// Count the block
|
||||
if (m_counting) {
|
||||
if (takeElse) {
|
||||
nodep->elsesp()->iterateAndNext(*this);
|
||||
nodep->elsesp()->iterateAndNextConst(*this);
|
||||
} else {
|
||||
nodep->ifsp()->iterateAndNext(*this);
|
||||
nodep->ifsp()->iterateAndNextConst(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,7 +166,7 @@ private:
|
||||
virtual void visit(AstCCall* nodep, AstNUser*) {
|
||||
//UINFO(4," CCALL "<<nodep<<endl);
|
||||
allNodes(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
if (m_fast) {
|
||||
// Enter the function and trace it
|
||||
nodep->funcp()->accept(*this);
|
||||
@@ -172,12 +175,12 @@ private:
|
||||
virtual void visit(AstCFunc* nodep, AstNUser*) {
|
||||
m_cfuncp = nodep;
|
||||
allNodes(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
m_cfuncp = NULL;
|
||||
}
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
allNodes(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->iterateChildrenConst(*this);
|
||||
}
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
||||
+4
-4
@@ -188,11 +188,11 @@ public:
|
||||
UINFO(9, " importIf se"<<(void*)this<<" from se"<<(void*)srcp<<endl);
|
||||
for (IdNameMap::const_iterator it=srcp->m_idNameMap.begin(); it!=srcp->m_idNameMap.end(); ++it) {
|
||||
const string& name = it->first;
|
||||
VSymEnt* srcp = it->second;
|
||||
VSymEnt* symp = new VSymEnt(graphp, srcp);
|
||||
reinsert(name, symp);
|
||||
VSymEnt* subSrcp = it->second;
|
||||
VSymEnt* subSymp = new VSymEnt(graphp, subSrcp);
|
||||
reinsert(name, subSymp);
|
||||
// And recurse to create children
|
||||
srcp->importFromIface(graphp, symp);
|
||||
subSymp->importFromIface(graphp, subSrcp);
|
||||
}
|
||||
}
|
||||
void cellErrorScopes(AstNode* lookp, string prettyName="") {
|
||||
|
||||
+5
-5
@@ -188,7 +188,7 @@ private:
|
||||
|
||||
// Change it variable
|
||||
FileLine* fl = nodep->fileline();
|
||||
AstNodeDType* dtypep
|
||||
AstNodeArrayDType* dtypep
|
||||
= new AstUnpackArrayDType (fl,
|
||||
nodep->findBitDType(m_outVarps.size(),
|
||||
m_outVarps.size(), AstNumeric::UNSIGNED),
|
||||
@@ -199,7 +199,7 @@ private:
|
||||
"__Vtablechg" + cvtToStr(m_modTables),
|
||||
dtypep);
|
||||
chgVarp->isConst(true);
|
||||
chgVarp->valuep(new AstInitArray (nodep->fileline(), NULL));
|
||||
chgVarp->valuep(new AstInitArray (nodep->fileline(), dtypep, NULL));
|
||||
m_modp->addStmtp(chgVarp);
|
||||
AstVarScope* chgVscp = new AstVarScope (chgVarp->fileline(), m_scopep, chgVarp);
|
||||
m_scopep->addVarp(chgVscp);
|
||||
@@ -236,7 +236,7 @@ private:
|
||||
AstVarScope* outvscp = *it;
|
||||
AstVar* outvarp = outvscp->varp();
|
||||
FileLine* fl = nodep->fileline();
|
||||
AstNodeDType* dtypep
|
||||
AstNodeArrayDType* dtypep
|
||||
= new AstUnpackArrayDType (fl, outvarp->dtypep(),
|
||||
new AstRange (fl, VL_MASK_I(m_inWidth), 0));
|
||||
v3Global.rootp()->typeTablep()->addTypesp(dtypep);
|
||||
@@ -246,7 +246,7 @@ private:
|
||||
dtypep);
|
||||
tablevarp->isConst(true);
|
||||
tablevarp->isStatic(true);
|
||||
tablevarp->valuep(new AstInitArray (nodep->fileline(), NULL));
|
||||
tablevarp->valuep(new AstInitArray (nodep->fileline(), dtypep, NULL));
|
||||
m_modp->addStmtp(tablevarp);
|
||||
AstVarScope* tablevscp = new AstVarScope(tablevarp->fileline(), m_scopep, tablevarp);
|
||||
m_scopep->addVarp(tablevscp);
|
||||
@@ -353,7 +353,7 @@ private:
|
||||
== var2p->dtypep()->arrayUnpackedElements())) {
|
||||
AstNode* init1p = var1p->valuep()->castInitArray();
|
||||
AstNode* init2p = var2p->valuep()->castInitArray();
|
||||
if (init1p->sameTree(init2p)) {
|
||||
if (init1p->sameGateTree(init2p)) {
|
||||
UINFO(8," Duplicate table var "<<vsc2p<<" == "<<vsc1p<<endl);
|
||||
vsc1p->unlinkFrBack()->deleteTree();
|
||||
return vsc2p;
|
||||
|
||||
+25
-15
@@ -43,7 +43,6 @@ private:
|
||||
// NODE STATE
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstScope* m_scopetopp; // Current top scope
|
||||
AstCFunc* m_initFuncp; // Trace function being built
|
||||
AstCFunc* m_initSubFuncp; // Trace function being built (under m_init)
|
||||
@@ -65,15 +64,19 @@ private:
|
||||
return level;
|
||||
}
|
||||
|
||||
const char* varIgnoreTrace(AstVar* nodep) {
|
||||
const char* vscIgnoreTrace(AstVarScope* nodep) {
|
||||
// Return true if this shouldn't be traced
|
||||
// See also similar rule in V3Coverage::varIgnoreToggle
|
||||
string prettyName = nodep->prettyName();
|
||||
if (!nodep->isTrace()) {
|
||||
AstVar* varp = nodep->varp();
|
||||
string prettyName = varp->prettyName();
|
||||
if (!varp->isTrace()) {
|
||||
return "Verilator trace_off";
|
||||
}
|
||||
else if (!nodep->isTrace()) {
|
||||
return "Verilator cell trace_off";
|
||||
}
|
||||
else if (!v3Global.opt.traceUnderscore()) {
|
||||
if (prettyName.c_str()[0] == '_')
|
||||
if (prettyName.size()>=1 && prettyName[0] == '_')
|
||||
return "Leading underscore";
|
||||
if (prettyName.find("._") != string::npos)
|
||||
return "Inlined leading underscore";
|
||||
@@ -108,12 +111,15 @@ private:
|
||||
void addCFuncStmt(AstCFunc* basep, AstNode* nodep, VNumRange arrayRange) {
|
||||
basep->addStmtsp(nodep);
|
||||
}
|
||||
void addTraceDecl(const VNumRange& arrayRange) {
|
||||
void addTraceDecl(const VNumRange& arrayRange,
|
||||
int widthOverride) { // If !=0, is packed struct/array where basicp size misreflects one element
|
||||
VNumRange bitRange;
|
||||
AstBasicDType* bdtypep = m_traValuep->dtypep()->basicp();
|
||||
if (bdtypep) bitRange = bdtypep->nrange();
|
||||
if (widthOverride) bitRange = VNumRange(widthOverride-1,0,false);
|
||||
else if (bdtypep) bitRange = bdtypep->nrange();
|
||||
AstTraceDecl* declp = new AstTraceDecl(m_traVscp->fileline(), m_traShowname, m_traValuep,
|
||||
bitRange, arrayRange);
|
||||
UINFO(9,"Decl "<<declp<<endl);
|
||||
|
||||
if (m_initSubStmts && v3Global.opt.outputSplitCTrace()
|
||||
&& m_initSubStmts > v3Global.opt.outputSplitCTrace()) {
|
||||
@@ -147,7 +153,8 @@ private:
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
if (!nodep->varp()->isTemp() && !nodep->varp()->isParam() && !nodep->varp()->isFuncLocal()) {
|
||||
// Avoid updating this if (), instead see varp->isTrace()
|
||||
if (!nodep->varp()->isTemp() && !nodep->varp()->isFuncLocal()) {
|
||||
UINFO(5, " vsc "<<nodep<<endl);
|
||||
AstVar* varp = nodep->varp();
|
||||
AstScope* scopep = nodep->scopep();
|
||||
@@ -160,8 +167,8 @@ private:
|
||||
|
||||
m_traVscp = nodep;
|
||||
m_traValuep = NULL;
|
||||
if (varIgnoreTrace(varp)) {
|
||||
addIgnore(varIgnoreTrace(varp));
|
||||
if (vscIgnoreTrace(nodep)) {
|
||||
addIgnore(vscIgnoreTrace(nodep));
|
||||
} else {
|
||||
++m_statSigs;
|
||||
if (nodep->valuep()) m_traValuep = nodep->valuep()->cloneTree(true);
|
||||
@@ -198,7 +205,7 @@ private:
|
||||
&& m_traVscp->dtypep()->skipRefp() == nodep) { // Nothing above this array
|
||||
// Simple 1-D array, use exising V3EmitC runtime loop rather than unrolling
|
||||
// This will put "(index)" at end of signal name for us
|
||||
addTraceDecl(nodep->declRange());
|
||||
addTraceDecl(nodep->declRange(), 0);
|
||||
} else {
|
||||
// Unroll now, as have no other method to get right signal names
|
||||
AstNodeDType* subtypep = nodep->subDTypep()->skipRefp();
|
||||
@@ -224,7 +231,7 @@ private:
|
||||
if (!v3Global.opt.traceStructs()) {
|
||||
// Everything downstream is packed, so deal with as one trace unit
|
||||
// This may not be the nicest for user presentation, but is a much faster way to trace
|
||||
addTraceDecl(VNumRange());
|
||||
addTraceDecl(VNumRange(), nodep->width());
|
||||
} else {
|
||||
AstNodeDType* subtypep = nodep->subDTypep()->skipRefp();
|
||||
for (int i=nodep->lsb(); i<=nodep->msb(); ++i) {
|
||||
@@ -249,7 +256,7 @@ private:
|
||||
if (nodep->packed() && !v3Global.opt.traceStructs()) {
|
||||
// Everything downstream is packed, so deal with as one trace unit
|
||||
// This may not be the nicest for user presentation, but is a much faster way to trace
|
||||
addTraceDecl(VNumRange());
|
||||
addTraceDecl(VNumRange(), nodep->width());
|
||||
} else {
|
||||
if (!nodep->packed()) {
|
||||
addIgnore("Unsupported: Unpacked struct/union");
|
||||
@@ -260,7 +267,6 @@ private:
|
||||
AstNode* oldValuep = m_traValuep;
|
||||
{
|
||||
m_traShowname += string(" ")+itemp->prettyName();
|
||||
m_traValuep->dumpTree(cout, "-tv: ");
|
||||
if (nodep->castStructDType()) {
|
||||
m_traValuep = new AstSel(nodep->fileline(), m_traValuep->cloneTree(true),
|
||||
itemp->lsb(), subtypep->width());
|
||||
@@ -279,7 +285,11 @@ private:
|
||||
}
|
||||
virtual void visit(AstBasicDType* nodep, AstNUser*) {
|
||||
if (m_traVscp) {
|
||||
addTraceDecl(VNumRange());
|
||||
if (nodep->keyword()==AstBasicDTypeKwd::STRING) {
|
||||
addIgnore("Unsupported: strings");
|
||||
} else {
|
||||
addTraceDecl(VNumRange(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep, AstNUser*) {
|
||||
|
||||
+26
-12
@@ -579,29 +579,29 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
nodep->addStmtp(enassp);
|
||||
|
||||
// now append this driver to the driver logic.
|
||||
AstNode* ref1p = new AstVarRef(nodep->fileline(), newlhsp,false);
|
||||
AstNode* ref2p = new AstVarRef(nodep->fileline(), newenp, false);
|
||||
andp = new AstAnd(nodep->fileline(), ref1p, ref2p);
|
||||
AstNode* ref1p = new AstVarRef(refp->fileline(), newlhsp,false);
|
||||
AstNode* ref2p = new AstVarRef(refp->fileline(), newenp, false);
|
||||
andp = new AstAnd(refp->fileline(), ref1p, ref2p);
|
||||
|
||||
// or this to the others
|
||||
orp = (!orp) ? andp : new AstOr(nodep->fileline(), orp, andp);
|
||||
orp = (!orp) ? andp : new AstOr(refp->fileline(), orp, andp);
|
||||
|
||||
if (envarp) {
|
||||
AstNode* ref3p = new AstVarRef(nodep->fileline(), newenp, false);
|
||||
AstNode* ref3p = new AstVarRef(refp->fileline(), newenp, false);
|
||||
enp = (!enp) ? ref3p : new AstOr(ref3p->fileline(), enp, ref3p);
|
||||
}
|
||||
AstNode* tmp = new AstNot(newenp->fileline(), new AstVarRef(newenp->fileline(), newenp, false));
|
||||
undrivenp = ((!undrivenp) ? tmp
|
||||
: new AstAnd(nodep->fileline(), tmp, undrivenp));
|
||||
: new AstAnd(refp->fileline(), tmp, undrivenp));
|
||||
}
|
||||
if (!undrivenp) { // No drivers on the bus
|
||||
V3Number ones(nodep->fileline(), lhsp->width()); ones.setAllBits1();
|
||||
undrivenp = new AstConst(nodep->fileline(), ones);
|
||||
V3Number ones(invarp->fileline(), lhsp->width()); ones.setAllBits1();
|
||||
undrivenp = new AstConst(invarp->fileline(), ones);
|
||||
}
|
||||
if (!outvarp) {
|
||||
// This is the final resolution of the tristate, so we apply
|
||||
// the pull direction to any undriven pins.
|
||||
V3Number pull(nodep->fileline(), lhsp->width());
|
||||
V3Number pull(invarp->fileline(), lhsp->width());
|
||||
AstPull* pullp = (AstPull*)lhsp->user3p();
|
||||
if (pullp && pullp->direction() == 1) {
|
||||
pull.setAllBits1();
|
||||
@@ -609,9 +609,9 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
} else {
|
||||
pull.setAllBits0(); // default pull direction is down.
|
||||
}
|
||||
undrivenp = new AstAnd(nodep->fileline(), undrivenp,
|
||||
new AstConst(nodep->fileline(), pull));
|
||||
orp = new AstOr(nodep->fileline(), orp, undrivenp);
|
||||
undrivenp = new AstAnd(invarp->fileline(), undrivenp,
|
||||
new AstConst(invarp->fileline(), pull));
|
||||
orp = new AstOr(invarp->fileline(), orp, undrivenp);
|
||||
} else {
|
||||
undrivenp->deleteTree(); undrivenp=NULL;
|
||||
}
|
||||
@@ -957,12 +957,26 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
}
|
||||
}
|
||||
}
|
||||
void visitEqNeqWild(AstNodeBiop* nodep) {
|
||||
if (!nodep->rhsp()->castConst()) {
|
||||
nodep->v3error("Unsupported: RHS of ==? or !=? must be constant to be synthesizable"); // Says spec.
|
||||
// rhs we want to keep X/Z intact, so otherwise ignore
|
||||
}
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
if (nodep->lhsp()->user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<<nodep->prettyTypeName()); return; }
|
||||
}
|
||||
virtual void visit(AstEqCase* nodep, AstNUser*) {
|
||||
visitCaseEq(nodep,false);
|
||||
}
|
||||
virtual void visit(AstNeqCase* nodep, AstNUser*) {
|
||||
visitCaseEq(nodep,true);
|
||||
}
|
||||
virtual void visit(AstEqWild* nodep, AstNUser*) {
|
||||
visitEqNeqWild(nodep);
|
||||
}
|
||||
virtual void visit(AstNeqWild* nodep, AstNUser*) {
|
||||
visitEqNeqWild(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstPull* nodep, AstNUser*) {
|
||||
UINFO(9,dbgState()<<nodep<<endl);
|
||||
|
||||
+5
-4
@@ -75,7 +75,7 @@ public:
|
||||
|
||||
private:
|
||||
// METHODS
|
||||
inline bool bitNumOk(int bit) const { return (bit*FLAGS_PER_BIT < (int)m_flags.size()); }
|
||||
inline bool bitNumOk(int bit) const { return bit>=0 && (bit*FLAGS_PER_BIT < (int)m_flags.size()); }
|
||||
inline bool usedFlag(int bit) const { return m_usedWhole || m_flags[bit*FLAGS_PER_BIT + FLAG_USED]; }
|
||||
inline bool drivenFlag(int bit) const { return m_drivenWhole || m_flags[bit*FLAGS_PER_BIT + FLAG_DRIVEN]; }
|
||||
enum BitNamesWhich { BN_UNUSED, BN_UNDRIVEN, BN_BOTH };
|
||||
@@ -145,9 +145,10 @@ public:
|
||||
return isUsedNotDrivenBit(0, m_flags.size()/FLAGS_PER_BIT);
|
||||
}
|
||||
bool unusedMatch(AstVar* nodep) {
|
||||
const char* regexpp = v3Global.opt.unusedRegexp().c_str();
|
||||
if (!regexpp || !*regexpp) return false;
|
||||
return VString::wildmatch(nodep->prettyName().c_str(), regexpp);
|
||||
string regexp = v3Global.opt.unusedRegexp();
|
||||
if (regexp == "") return false;
|
||||
string prettyName = nodep->prettyName();
|
||||
return VString::wildmatch(prettyName.c_str(), regexp.c_str());
|
||||
}
|
||||
void reportViolations() {
|
||||
// Combine bits into overall state
|
||||
|
||||
+13
-13
@@ -126,7 +126,7 @@ private:
|
||||
else {
|
||||
string name = ((string)"__Vlvbound"+cvtToStr(m_modp->varNumGetInc()));
|
||||
AstVar* varp = new AstVar(fl, AstVarType::MODULETEMP, name,
|
||||
VFlagLogicPacked(), prep->width());
|
||||
prep->dtypep());
|
||||
m_modp->addStmtp(varp);
|
||||
|
||||
AstNode* abovep = prep->backp(); // Grab above point before lose it w/ next replace
|
||||
@@ -163,13 +163,13 @@ private:
|
||||
m_assignwp = NULL;
|
||||
}
|
||||
virtual void visit(AstCaseItem* nodep, AstNUser*) {
|
||||
m_constXCvt = false; // Avoid loosing the X's in casex
|
||||
m_constXCvt = false; // Avoid losing the X's in casex
|
||||
nodep->condsp()->iterateAndNext(*this);
|
||||
m_constXCvt = true;
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep, AstNUser*) {
|
||||
m_constXCvt = false; // Avoid loosing the X's in casex
|
||||
m_constXCvt = false; // Avoid losing the X's in casex
|
||||
nodep->iterateChildren(*this);
|
||||
m_constXCvt = true;
|
||||
}
|
||||
@@ -337,14 +337,14 @@ private:
|
||||
// Find range of dtype we are selecting from
|
||||
// Similar code in V3Const::warnSelect
|
||||
int maxmsb = nodep->fromp()->dtypep()->width()-1;
|
||||
int maxlsb = maxmsb - nodep->width() + 1;
|
||||
if (debug()>=9) nodep->dumpTree(cout,"sel_old: ");
|
||||
V3Number maxlsbnum (nodep->fileline(), nodep->lsbp()->width(), maxlsb);
|
||||
V3Number maxmsbnum (nodep->fileline(), nodep->lsbp()->width(), maxmsb);
|
||||
|
||||
// See if the condition is constant true
|
||||
AstNode* condp = new AstLte (nodep->fileline(),
|
||||
nodep->lsbp()->cloneTree(false),
|
||||
new AstConst(nodep->fileline(), maxlsbnum));
|
||||
// If (maxmsb >= selected), we're in bound
|
||||
AstNode* condp = new AstGte (nodep->fileline(),
|
||||
new AstConst(nodep->fileline(), maxmsbnum),
|
||||
nodep->lsbp()->cloneTree(false));
|
||||
// See if the condition is constant true (e.g. always in bound due to constant select)
|
||||
// Note below has null backp(); the Edit function knows how to deal with that.
|
||||
condp = V3Const::constifyEdit(condp);
|
||||
if (condp->isOne()) {
|
||||
@@ -352,7 +352,7 @@ private:
|
||||
condp->deleteTree();
|
||||
}
|
||||
else if (!lvalue) {
|
||||
// SEL(...) -> COND(LTE(bit<=maxlsb), ARRAYSEL(...), {width{1'bx}})
|
||||
// SEL(...) -> COND(LTE(bit<=maxmsb), ARRAYSEL(...), {width{1'bx}})
|
||||
AstNRelinker replaceHandle;
|
||||
nodep->unlinkFrBack(&replaceHandle);
|
||||
V3Number xnum (nodep->fileline(), nodep->width());
|
||||
@@ -400,9 +400,9 @@ private:
|
||||
V3Number widthnum (nodep->fileline(), nodep->bitp()->width(), declElements-1);
|
||||
|
||||
// See if the condition is constant true
|
||||
AstNode* condp = new AstLte (nodep->fileline(),
|
||||
nodep->bitp()->cloneTree(false),
|
||||
new AstConst(nodep->fileline(), widthnum));
|
||||
AstNode* condp = new AstGte (nodep->fileline(),
|
||||
new AstConst(nodep->fileline(), widthnum),
|
||||
nodep->bitp()->cloneTree(false));
|
||||
// Note below has null backp(); the Edit function knows how to deal with that.
|
||||
condp = V3Const::constifyEdit(condp);
|
||||
if (condp->isOne()) {
|
||||
|
||||
+33
-16
@@ -73,6 +73,7 @@ private:
|
||||
nodep->v3error("Unsupported: Can't unroll generate for; "<<reason);
|
||||
}
|
||||
UINFO(3," Can't Unroll: "<<reason<<" :"<<nodep<<endl);
|
||||
//if (debug()>=9) nodep->dumpTree(cout,"-cant-");
|
||||
V3Stats::addStatSum(string("Unrolling gave up, ")+reason, 1);
|
||||
return false;
|
||||
}
|
||||
@@ -158,20 +159,32 @@ private:
|
||||
if (!constIncp) return cantUnroll(nodep, "non-constant increment");
|
||||
if (constIncp->isZero()) return cantUnroll(nodep, "zero increment"); // Or we could loop forever below...
|
||||
|
||||
bool lt = condp->castLt() || condp->castLtS();
|
||||
bool lt = condp->castLt() || condp->castLtS();
|
||||
bool lte = condp->castLte() || condp->castLteS();
|
||||
bool gt = condp->castGt() || condp->castGtS();
|
||||
bool gt = condp->castGt() || condp->castGtS();
|
||||
bool gte = condp->castGte() || condp->castGteS();
|
||||
if (!lt && !lte && !gt && !gte)
|
||||
return cantUnroll(nodep, "condition not <= or <");
|
||||
AstNodeBiop* condBip = condp->castNodeBiop();
|
||||
if (!condBip->lhsp()->castVarRef())
|
||||
return cantUnroll(nodep, "no variable on lhs of condition");
|
||||
if (condBip->lhsp()->castVarRef()->varp() != m_forVarp
|
||||
|| condBip->lhsp()->castVarRef()->varScopep() != m_forVscp)
|
||||
return cantUnroll(nodep, "condition not <=, <, >= or >");
|
||||
AstNodeBiop* cmpInstrp = condp->castNodeBiop();
|
||||
bool cmpVarLhs;
|
||||
if (cmpInstrp->lhsp()->castVarRef()
|
||||
&& cmpInstrp->lhsp()->castVarRef()->varp() == m_forVarp
|
||||
&& cmpInstrp->lhsp()->castVarRef()->varScopep() == m_forVscp) {
|
||||
cmpVarLhs = true;
|
||||
} else if (cmpInstrp->rhsp()->castVarRef()
|
||||
&& cmpInstrp->rhsp()->castVarRef()->varp() == m_forVarp
|
||||
&& cmpInstrp->rhsp()->castVarRef()->varScopep() == m_forVscp) {
|
||||
cmpVarLhs = false;
|
||||
} else if (!cmpInstrp->rhsp()->castVarRef()) {
|
||||
return cantUnroll(nodep, "no variable on rhs of condition");
|
||||
} else {
|
||||
return cantUnroll(nodep, "different variable in condition");
|
||||
if (m_generate) V3Const::constifyParamsEdit(condBip->rhsp()); // rhsp may change
|
||||
AstConst* constStopp = condBip->rhsp()->castConst();
|
||||
}
|
||||
|
||||
if (m_generate) V3Const::constifyParamsEdit(cmpVarLhs ? cmpInstrp->rhsp()
|
||||
: cmpInstrp->lhsp()); // rhsp/lhsp may change
|
||||
AstConst* constStopp = (cmpVarLhs ? cmpInstrp->rhsp()->castConst()
|
||||
: cmpInstrp->lhsp()->castConst());
|
||||
if (!constStopp) return cantUnroll(nodep, "non-constant final value");
|
||||
UINFO(8, " Stop expr ok: "<<constStopp<<endl);
|
||||
//
|
||||
@@ -181,7 +194,7 @@ private:
|
||||
return cantUnroll(nodep, "init/final/increment too large or four state");
|
||||
vlsint32_t valInit = constInitp->num().toSInt();
|
||||
vlsint32_t valStop = constStopp->num().toSInt();
|
||||
if (lte) valStop++; if (gte) valStop--;
|
||||
if (gte) valStop++; if (lte) valStop--; // 23 >= a, handle as if 24 > a
|
||||
vlsint32_t valInc = constIncp->num().toSInt();
|
||||
if (subtract) valInc = -valInc;
|
||||
UINFO(8," In Numbers: for (v="<<valInit<<"; v<"<<valStop<<"; v=v+"<<valInc<<")\n");
|
||||
@@ -216,9 +229,9 @@ private:
|
||||
if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop");
|
||||
//
|
||||
// Finally, we can do it
|
||||
forUnroller(nodep, initp, precondsp, condp, incp, bodysp,
|
||||
forUnroller(nodep, initp, precondsp, incp, bodysp,
|
||||
constInitp->num(),
|
||||
condBip, constStopp->num(),
|
||||
cmpInstrp, constStopp->num(), cmpVarLhs,
|
||||
incInstrp, constIncp->num()); nodep = NULL;
|
||||
// Cleanup
|
||||
return true;
|
||||
@@ -226,10 +239,10 @@ private:
|
||||
|
||||
void forUnroller(AstNode* nodep,
|
||||
AstNode* initp,
|
||||
AstNode* precondsp, AstNode* condp,
|
||||
AstNode* precondsp,
|
||||
AstNode* incp, AstNode* bodysp,
|
||||
const V3Number& numInit,
|
||||
AstNodeBiop* cmpInstrp, const V3Number& numStop,
|
||||
AstNodeBiop* cmpInstrp, const V3Number& numStop, bool cmpVarLhs,
|
||||
AstNodeBiop* incInstrp, const V3Number& numInc) {
|
||||
UINFO(4, " Unroll for var="<<numInit<<"; var<"<<numStop<<"; var+="<<numInc<<endl);
|
||||
UINFO(6, " cmpI "<<cmpInstrp<<endl);
|
||||
@@ -269,7 +282,11 @@ private:
|
||||
UINFO(8," Looping "<<loopValue<<endl);
|
||||
// if loopValue<valStop
|
||||
V3Number contin (nodep->fileline(), 1);
|
||||
cmpInstrp->numberOperate(contin, loopValue, numStop);
|
||||
if (cmpVarLhs) {
|
||||
cmpInstrp->numberOperate(contin, loopValue, numStop);
|
||||
} else {
|
||||
cmpInstrp->numberOperate(contin, numStop, loopValue);
|
||||
}
|
||||
if (contin.isEqZero()) {
|
||||
break; // Done with the loop
|
||||
} else {
|
||||
|
||||
+1221
-747
File diff suppressed because it is too large
Load Diff
+17
-7
@@ -69,6 +69,22 @@ class WidthCommitVisitor : public AstNVisitor {
|
||||
// AstVar::user1p -> bool, processed
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
static AstConst* newIfConstCommitSize (AstConst* nodep) {
|
||||
if ((nodep->dtypep()->width() != nodep->num().width())
|
||||
|| !nodep->num().sized()) { // Need to force the number rrom unsized to sized
|
||||
V3Number num (nodep->fileline(), nodep->dtypep()->width());
|
||||
num.opAssign(nodep->num());
|
||||
num.isSigned(nodep->isSigned());
|
||||
AstConst* newp = new AstConst(nodep->fileline(), num);
|
||||
newp->dtypeFrom(nodep);
|
||||
return newp;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// METHODS
|
||||
void editDType(AstNode* nodep) {
|
||||
@@ -97,13 +113,7 @@ private:
|
||||
virtual void visit(AstConst* nodep, AstNUser*) {
|
||||
if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype");
|
||||
nodep->dtypep()->accept(*this); // Do datatype first
|
||||
if ((nodep->dtypep()->width() != nodep->num().width())
|
||||
|| !nodep->num().sized()) { // Need to force the number rrom unsized to sized
|
||||
V3Number num (nodep->fileline(), nodep->dtypep()->width());
|
||||
num.opAssign(nodep->num());
|
||||
num.isSigned(nodep->isSigned());
|
||||
AstConst* newp = new AstConst(nodep->fileline(), num);
|
||||
newp->dtypeFrom(nodep);
|
||||
if (AstConst* newp = newIfConstCommitSize(nodep)) {
|
||||
nodep->replaceWith(newp);
|
||||
AstNode* oldp = nodep; nodep = newp;
|
||||
//if (debug()>4) oldp->dumpTree(cout," fixConstSize_old: ");
|
||||
|
||||
+35
-29
@@ -175,6 +175,22 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeDType* sliceDType(AstPackArrayDType* nodep, int msb, int lsb) {
|
||||
// Return slice needed for msb/lsb, either as original dtype or a new slice dtype
|
||||
if (nodep->declRange().elements() == (msb-lsb+1) // Extracting whole of original array
|
||||
&& nodep->declRange().lo() == lsb) {
|
||||
return nodep;
|
||||
} else {
|
||||
// Need a slice data type, which is an array of the extracted type, but with (presumably) different size
|
||||
VNumRange newRange (msb, lsb, nodep->declRange().littleEndian());
|
||||
AstNodeDType* vardtypep = new AstPackArrayDType(nodep->fileline(),
|
||||
nodep->subDTypep(), // Need to strip off array reference
|
||||
new AstRange(nodep->fileline(), newRange));
|
||||
v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
|
||||
return vardtypep;
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
// If adding new visitors, insure V3Width's visit(TYPE) calls into here
|
||||
|
||||
@@ -302,18 +318,7 @@ private:
|
||||
new AstConst(nodep->fileline(),AstConst::Unsized32(),(msb-lsb+1)*elwidth));
|
||||
newp->declRange(fromRange);
|
||||
newp->declElWidth(elwidth);
|
||||
if (fromRange.elements() == (msb-lsb+1) // Extracting whole of original array
|
||||
&& fromRange.lo() == lsb) {
|
||||
newp->dtypeFrom(adtypep);
|
||||
} else {
|
||||
// Need a slice data type, which is an array of the extracted type, but with (presumably) different size
|
||||
VNumRange newRange (msb, lsb, fromRange.littleEndian());
|
||||
AstNodeDType* vardtypep = new AstPackArrayDType(nodep->fileline(),
|
||||
adtypep->subDTypep(), // Need to strip off array reference
|
||||
new AstRange(nodep->fileline(), newRange));
|
||||
v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
|
||||
newp->dtypeFrom(vardtypep);
|
||||
}
|
||||
newp->dtypeFrom(sliceDType(adtypep, msb, lsb));
|
||||
//if (debug()>=9) newp->dumpTree(cout,"--EXTBTn: ");
|
||||
if (newp->widthMin()!=(int)newp->widthConst()) nodep->v3fatalSrc("Width mismatch");
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
|
||||
@@ -388,42 +393,43 @@ private:
|
||||
AstNodeDType* ddtypep = fromdata.m_dtypep;
|
||||
VNumRange fromRange = fromdata.m_fromRange;
|
||||
if (ddtypep->castBasicDType()
|
||||
|| ddtypep->castPackArrayDType()
|
||||
|| (ddtypep->castNodeClassDType()
|
||||
&& ddtypep->castNodeClassDType()->packedUnsup())) {
|
||||
AstSel* newp = NULL;
|
||||
int elwidth = 1;
|
||||
AstNode* newwidthp = widthp;
|
||||
if (AstPackArrayDType* adtypep = ddtypep->castPackArrayDType()) {
|
||||
elwidth = adtypep->width() / fromRange.elements();
|
||||
newwidthp = new AstConst (nodep->fileline(),AstConst::Unsized32(), width * elwidth);
|
||||
}
|
||||
AstNode* newlsbp = NULL;
|
||||
if (nodep->castSelPlus()) {
|
||||
if (fromRange.littleEndian()) {
|
||||
// SELPLUS(from,lsb,width) -> SEL(from, (vector_msb-width+1)-sel, width)
|
||||
newp = new AstSel (nodep->fileline(),
|
||||
fromp,
|
||||
newSubNeg((fromRange.hi()-width+1), rhsp),
|
||||
widthp);
|
||||
newlsbp = newSubNeg((fromRange.hi()-width+1), rhsp);
|
||||
} else {
|
||||
// SELPLUS(from,lsb,width) -> SEL(from, lsb-vector_lsb, width)
|
||||
newp = new AstSel (nodep->fileline(),
|
||||
fromp,
|
||||
newSubNeg(rhsp, fromRange.lo()),
|
||||
widthp);
|
||||
newlsbp = newSubNeg(rhsp, fromRange.lo());
|
||||
}
|
||||
} else if (nodep->castSelMinus()) {
|
||||
if (fromRange.littleEndian()) {
|
||||
// SELMINUS(from,msb,width) -> SEL(from, msb-[bit])
|
||||
newp = new AstSel (nodep->fileline(),
|
||||
fromp,
|
||||
newSubNeg(fromRange.hi(), rhsp),
|
||||
widthp);
|
||||
newlsbp = newSubNeg(fromRange.hi(), rhsp);
|
||||
} else {
|
||||
// SELMINUS(from,msb,width) -> SEL(from, msb-(width-1)-lsb#)
|
||||
newp = new AstSel (nodep->fileline(),
|
||||
fromp,
|
||||
newSubNeg(rhsp, fromRange.lo()+(width-1)),
|
||||
widthp);
|
||||
newlsbp = newSubNeg(rhsp, fromRange.lo()+(width-1));
|
||||
}
|
||||
} else {
|
||||
nodep->v3fatalSrc("Bad Case");
|
||||
}
|
||||
if (elwidth != 1) newlsbp = new AstMul (nodep->fileline(), newlsbp,
|
||||
new AstConst (nodep->fileline(), elwidth));
|
||||
AstSel* newp = new AstSel (nodep->fileline(),
|
||||
fromp, newlsbp, newwidthp);
|
||||
newp->declRange(fromRange);
|
||||
newp->declElWidth(elwidth);
|
||||
UINFO(6," new "<<newp<<endl);
|
||||
if (debug()>=9) newp->dumpTree(cout,"--SELNEW: ");
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
|
||||
}
|
||||
else { // NULL=bad extract, or unknown node type
|
||||
|
||||
+5
-8
@@ -116,8 +116,8 @@ void V3Global::readFiles() {
|
||||
|
||||
V3Parse parser (v3Global.rootp(), &filter, &parseSyms);
|
||||
// Read top module
|
||||
for (V3StringList::const_iterator it = v3Global.opt.vFiles().begin();
|
||||
it != v3Global.opt.vFiles().end(); ++it) {
|
||||
const V3StringList& vFiles = v3Global.opt.vFiles();
|
||||
for (V3StringList::const_iterator it = vFiles.begin(); it != vFiles.end(); ++it) {
|
||||
string filename = *it;
|
||||
parser.parseFile(new FileLine("COMMAND_LINE",0), filename, false,
|
||||
"Cannot find file containing module: ");
|
||||
@@ -126,8 +126,8 @@ void V3Global::readFiles() {
|
||||
// Read libraries
|
||||
// To be compatible with other simulators,
|
||||
// this needs to be done after the top file is read
|
||||
for (V3StringSet::const_iterator it = v3Global.opt.libraryFiles().begin();
|
||||
it != v3Global.opt.libraryFiles().end(); ++it) {
|
||||
const V3StringSet& libraryFiles = v3Global.opt.libraryFiles();
|
||||
for (V3StringSet::const_iterator it = libraryFiles.begin(); it != libraryFiles.end(); ++it) {
|
||||
string filename = *it;
|
||||
parser.parseFile(new FileLine("COMMAND_LINE",0), filename, true,
|
||||
"Cannot find file containing library module: ");
|
||||
@@ -256,6 +256,7 @@ void process () {
|
||||
// Move assignments from X into MODULE temps.
|
||||
// (Before flattening, so each new X variable is shared between all scopes of that module.)
|
||||
V3Unknown::unknownAll(v3Global.rootp());
|
||||
v3Global.constRemoveXs(true);
|
||||
V3Global::dumpCheckGlobalTree("unknown.tree");
|
||||
|
||||
// Module inlining
|
||||
@@ -435,11 +436,9 @@ void process () {
|
||||
V3Order::orderAll(v3Global.rootp());
|
||||
V3Global::dumpCheckGlobalTree("order.tree");
|
||||
|
||||
#ifndef NEW_ORDERING
|
||||
// Change generated clocks to look at delayed signals
|
||||
V3GenClk::genClkAll(v3Global.rootp());
|
||||
V3Global::dumpCheckGlobalTree("genclk.tree");
|
||||
#endif
|
||||
|
||||
// Convert sense lists into IF statements.
|
||||
V3Clock::clockAll(v3Global.rootp());
|
||||
@@ -464,11 +463,9 @@ void process () {
|
||||
V3Dead::deadifyAll(v3Global.rootp());
|
||||
V3Global::dumpCheckGlobalTree("const.tree");
|
||||
|
||||
#ifndef NEW_ORDERING
|
||||
// Detect change loop
|
||||
V3Changed::changedAll(v3Global.rootp());
|
||||
V3Global::dumpCheckGlobalTree("changed.tree");
|
||||
#endif
|
||||
|
||||
// Create tracing logic, since we ripped out some signals the user might want to trace
|
||||
// Note past this point, we presume traced variables won't move between CFuncs
|
||||
|
||||
+10
-2
@@ -424,6 +424,11 @@ sub tree_line {
|
||||
($typefunc->{uinfo} = $func) =~ s/[ \t\"\{\}]+/ /g;
|
||||
push @{$self->{treeop}{$type}}, $typefunc;
|
||||
}
|
||||
elsif ($func =~ /TREE_SKIP_VISIT\s*\(\s* \"([^\"]*)\" \s*\)/sx) {
|
||||
my $type = $1;
|
||||
$self->{tree_skip_visit}{$type} = 1;
|
||||
$::Classes{$type} or $self->error("Unknown node type: $type");
|
||||
}
|
||||
else {
|
||||
$self->error("Unknown astgen op: $func");
|
||||
}
|
||||
@@ -598,9 +603,12 @@ sub tree_base {
|
||||
@out_for_type,
|
||||
" }\n") if ($out_for_type[0]);
|
||||
} elsif ($out_for_type[0]) { # Other types with something to print
|
||||
my $skip = $self->{tree_skip_visit}{$type};
|
||||
my $gen = $skip ? "Gen" : "";
|
||||
$self->print(" // Generated by astgen\n",
|
||||
" virtual void visit(Ast${type}* nodep, AstNUser*) {\n",
|
||||
" nodep->iterateChildren(*this);\n",
|
||||
" virtual void visit$gen(Ast${type}* nodep, AstNUser*) {\n",
|
||||
($skip?"":
|
||||
" nodep->iterateChildren(*this);\n"),
|
||||
@out_for_type,
|
||||
" }\n");
|
||||
}
|
||||
|
||||
+6
-4
@@ -9,7 +9,7 @@ use Pod::Usage;
|
||||
use strict;
|
||||
use vars qw ($Debug $VERSION);
|
||||
|
||||
$VERSION = '3.318';
|
||||
$VERSION = '3.404';
|
||||
|
||||
our $Self;
|
||||
|
||||
@@ -304,10 +304,12 @@ sub clean_input {
|
||||
foreach my $line (@linesin) {
|
||||
$l++;
|
||||
if ($line =~ /BISONPRE_VERSION/) {
|
||||
($line =~ /BISONPRE_VERSION\((\S+)\s*,\s*([^\),]+)\)\s*$/)
|
||||
# 1 3 4
|
||||
($line =~ /BISONPRE_VERSION\((\S+)\s*,\s*((\S+)\s*,)?\s*([^\),]+)\)\s*$/)
|
||||
or die "%Error: $filename:$l: Bad form of BISONPRE_VERSION: $line\n";
|
||||
my $ver=$1; my $cmd=$2;
|
||||
if ($Self->{bison_version} >= $1) {
|
||||
my $ver=$1; my $ver_max=$3; my $cmd=$4;
|
||||
if ($Self->{bison_version} >= $1
|
||||
&& (!$ver_max || $Self->{bison_version} <= $ver_max)) {
|
||||
$line = $cmd."\n";
|
||||
} else {
|
||||
$line = "//NOP: $line";
|
||||
|
||||
Executable
+183
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/perl -w
|
||||
# See copyright, etc in below POD section.
|
||||
######################################################################
|
||||
|
||||
require 5.006_001;
|
||||
use Getopt::Long;
|
||||
use IO::File;
|
||||
use Pod::Usage;
|
||||
use strict;
|
||||
use vars qw ($Debug $VERSION);
|
||||
|
||||
$VERSION = '3.857';
|
||||
|
||||
#======================================================================
|
||||
# main
|
||||
|
||||
our $Opt_Debug;
|
||||
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
Getopt::Long::config ("no_auto_abbrev","pass_through");
|
||||
our @Opt_Args = ("cppcheck", @ARGV);
|
||||
if (! GetOptions (
|
||||
# Local options
|
||||
"help" => \&usage,
|
||||
"version" => sub { print "Version $VERSION\n"; system("cppcheck","--version"); exit(0); },
|
||||
)) {
|
||||
die "%Error: Bad usage, try 'cppcheck_filtered --help'\n";
|
||||
}
|
||||
|
||||
process();
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
sub usage {
|
||||
print "Version $VERSION\n";
|
||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT, -noperldoc=>1);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub process {
|
||||
my $cmd = join(' ',@Opt_Args);
|
||||
print "\t$cmd\n" if $Debug;
|
||||
my $fh = IO::File->new("$cmd 2>&1 |");
|
||||
my %uniq;
|
||||
my %errs;
|
||||
while (defined(my $line = $fh->getline())) {
|
||||
$line =~ s/Checking usage of global functions\.+//; # Sometimes tacked at end-of-line
|
||||
# General gunk
|
||||
next if $uniq{$line}++;
|
||||
next if $line =~ m!^<\?xml version!;
|
||||
next if $line =~ m!^<results>!;
|
||||
next if $line =~ m!^</results>!;
|
||||
next if $line =~ m!^<error.*id="unmatchedSuppression"!; # --suppress=unmatchedSuppression doesn't work
|
||||
next if $line =~ m!Cppcheck cannot find all the include files!; # An earlier id line is more specific
|
||||
next if $line =~ m!^Checking !;
|
||||
next if $line =~ m!^make.*Entering directory !;
|
||||
next if $line =~ m!^make.*Leaving directory !;
|
||||
next if $line =~ m!^\s+$!g;
|
||||
# Specific suppressions
|
||||
next if $line =~ m!id="missingInclude" .*systemc.h!;
|
||||
next if $line =~ m!id="missingInclude" .*svdpi.h!;
|
||||
next if $line =~ m!file=".*obj_dbg/V3ParseBison.c".* id="unreachableCode"!;
|
||||
# Output
|
||||
if ($line =~ /^cppcheck --/) {
|
||||
print $line if $Debug;
|
||||
} else {
|
||||
my $suppress;
|
||||
if ($line =~ /file="([^"]+)"\s+line="(\d+)"\s+id="([^"]+)"/) {
|
||||
my $file = $1; my $linenum = $2; my $id = $3;
|
||||
$suppress = 1 if _suppress($file,$linenum,$id);
|
||||
}
|
||||
if (!$suppress) {
|
||||
my $eline = "%Error: cppcheck: $line";
|
||||
print $eline;
|
||||
$errs{$eline}++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scalar(keys %errs)) {
|
||||
#my $all = join('',sort(keys %errs));
|
||||
#$Self->error("Cppcheck errors:\n$all");
|
||||
die "%Error: cppcheck_filtered found errors";
|
||||
}
|
||||
}
|
||||
|
||||
######################################################################
|
||||
|
||||
sub _suppress {
|
||||
my $filename = shift;
|
||||
my $linenum = shift;
|
||||
my $id = shift;
|
||||
#print "-Suppression search $filename $linenum $id\n" if $Self->{verbose};
|
||||
my $fh = IO::File->new("<$filename");
|
||||
if (!$fh) {
|
||||
warn "%Warning: $! $filename,";
|
||||
return undef;
|
||||
}
|
||||
my $l = 0;
|
||||
while (defined(my $line = $fh->getline())) {
|
||||
++$l;
|
||||
if ($l+1 == $linenum) {
|
||||
if ($line =~ /cppcheck-suppress\s+(\S+)/) {
|
||||
my $supid = $1;
|
||||
if ($supid eq $id) {
|
||||
return 1;
|
||||
} else {
|
||||
warn "%Warning: $filename: $l: Found suppress for id='$supid', not expected id='$id'\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
#######################################################################
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
cppcheck_filtered - cppcheck wrapper with post-processing
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
cppcheck_filtered ...normal cpp check flags...
|
||||
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Cppcheck_Filtered is a wrapper for cppcheck that filters out unnecessary
|
||||
warnings related to Verilator.
|
||||
|
||||
=head1 ARGUMENTS
|
||||
|
||||
Most arguments are passed through to cppcheck
|
||||
|
||||
=over 4
|
||||
|
||||
=item --help
|
||||
|
||||
Displays this message and program version and exits.
|
||||
|
||||
=item --version
|
||||
|
||||
Print the version number and exit.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
This is part of the L<http://www.veripool.org/> free Verilog EDA software
|
||||
tool suite. The latest version is available from CPAN and from
|
||||
L<http://www.veripool.org/>.
|
||||
|
||||
Copyright 2014-2014 by Wilson Snyder. This package 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 program 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.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Wilson Snyder <[email protected]>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
C<cppcheck>
|
||||
|
||||
=cut
|
||||
|
||||
######################################################################
|
||||
### Local Variables:
|
||||
### compile-command: "./cppcheck_filtered --xml V3Width.cpp"
|
||||
### End:
|
||||
+49
-44
@@ -43,7 +43,8 @@ extern void yyerrorf(const char* format, ...);
|
||||
//======================================================================
|
||||
|
||||
#define NEXTLINE() {PARSEP->linenoInc();}
|
||||
#define LINECHECK() { const char* cp=yytext; for (int n=yyleng; n; --n) if (cp[n]=='\n') NEXTLINE(); }
|
||||
#define LINECHECKS(textp,len) { const char* cp=textp; for (int n=len; n; --n) if (cp[n]=='\n') NEXTLINE(); }
|
||||
#define LINECHECK() LINECHECKS(yytext,yyleng)
|
||||
#define CRELINE() (PARSEP->copyOrSameFileLine())
|
||||
|
||||
#define FL { yylval.fl = CRELINE(); }
|
||||
@@ -137,7 +138,7 @@ void yyerrorf(const char* format, ...) {
|
||||
|
||||
%s V95 V01 V05 S05 S09 S12
|
||||
%s STRING ATTRMODE TABLE
|
||||
%s VA5 SA9 PSL VLT
|
||||
%s VA5 SAX PSL VLT
|
||||
%s SYSCHDR SYSCINT SYSCIMP SYSCIMPH SYSCCTOR SYSCDTOR
|
||||
%s IGNORE
|
||||
|
||||
@@ -149,6 +150,13 @@ id [a-zA-Z_][a-zA-Z0-9_$]*
|
||||
/* escaped identifier */
|
||||
escid \\[^ \t\f\r\n]+
|
||||
word [a-zA-Z0-9_]+
|
||||
/* verilog numbers, constructed to not match the ' that begins a '( or '{ */
|
||||
vnum1 [0-9]*?['']s?[bcodhBCODH][ \t\n]*[A-Fa-f0-9xXzZ_?]*
|
||||
vnum2 [0-9]*?['']s?[01xXzZ]
|
||||
vnum3 [0-9][_0-9]*[ \t\n]*['']s?[bcodhBCODH]?[ \t]*[A-Fa-f0-9xXzZ_?]+
|
||||
vnum4 [0-9][_0-9]*[ \t\n]*['']s?[bcodhBCODH]
|
||||
vnum5 [0-9][_0-9]*[ \t\n]*['']s
|
||||
vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
||||
|
||||
%%
|
||||
|
||||
@@ -171,7 +179,7 @@ word [a-zA-Z0-9_]+
|
||||
|
||||
/************************************************************************/
|
||||
/* Verilog 1995 */
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SAX,PSL>{
|
||||
{ws} { } /* otherwise ignore white-space */
|
||||
{crnl} { NEXTLINE(); } /* Count line numbers */
|
||||
/* Extensions to Verilog set, some specified by PSL */
|
||||
@@ -344,7 +352,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* Verilog 2001 */
|
||||
<V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
<V01,V05,VA5,S05,S09,S12,SAX,PSL>{
|
||||
/* System Tasks */
|
||||
"$signed" { FL; return yD_SIGNED; }
|
||||
"$unsigned" { FL; return yD_UNSIGNED; }
|
||||
@@ -375,13 +383,13 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* Verilog 2005 */
|
||||
<V05,S05,S09,S12,SA9,PSL>{
|
||||
<V05,S05,S09,S12,SAX,PSL>{
|
||||
/* Keywords */
|
||||
"uwire" { FL; return yWIRE; }
|
||||
}
|
||||
|
||||
/* System Verilog 2005 */
|
||||
<S05,S09,S12,PSL>{
|
||||
<S05,S09,S12,SAX,PSL>{
|
||||
/* System Tasks */
|
||||
"$bits" { FL; return yD_BITS; }
|
||||
"$clog2" { FL; return yD_CLOG2; }
|
||||
@@ -498,7 +506,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* SystemVerilog 2005 ONLY not PSL; different rules for PSL as specified below */
|
||||
<S05,S09,S12>{
|
||||
<S05,S09,S12,SAX>{
|
||||
/* Keywords */
|
||||
"assert" { FL; return yASSERT; }
|
||||
"const" { FL; return yCONST__LEX; }
|
||||
@@ -513,7 +521,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* SystemVerilog 2009 */
|
||||
<S09,S12,PSL>{
|
||||
<S09,S12,SAX,PSL>{
|
||||
/* Keywords */
|
||||
"global" { FL; return yGLOBAL__LEX; }
|
||||
"unique0" { FL; return yUNIQUE0; }
|
||||
@@ -542,7 +550,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* System Verilog 2012 */
|
||||
<S12,PSL>{
|
||||
<S12,SAX,PSL>{
|
||||
/* Keywords */
|
||||
"implements" { yyerrorf("Unsupported: SystemVerilog 2012 reserved word not implemented: %s",yytext); }
|
||||
"interconnect" { yyerrorf("Unsupported: SystemVerilog 2012 reserved word not implemented: %s",yytext); }
|
||||
@@ -551,7 +559,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* Default PLI rule */
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SAX,PSL>{
|
||||
"$"[a-zA-Z_$][a-zA-Z0-9_$]* { string str (yytext,yyleng);
|
||||
yylval.strp = PARSEP->newString(AstNode::encodeName(str));
|
||||
// Lookup unencoded name including the $, to avoid hitting normal signals
|
||||
@@ -564,7 +572,7 @@ word [a-zA-Z0-9_]+
|
||||
/************************************************************************/
|
||||
/* AMS */
|
||||
|
||||
<VA5,SA9>{
|
||||
<VA5,SAX>{
|
||||
/* Generic unsupported warnings */
|
||||
"above" { yyerrorf("Unsupported: AMS reserved word not implemented: %s",yytext); }
|
||||
"abs" { yyerrorf("Unsupported: AMS reserved word not implemented: %s",yytext); }
|
||||
@@ -658,7 +666,7 @@ word [a-zA-Z0-9_]+
|
||||
/* PSL */
|
||||
|
||||
/*Entry into PSL; mode change */
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SAX>{
|
||||
"psl" { yy_push_state(PSL); FL; return yPSL; }
|
||||
}
|
||||
|
||||
@@ -747,7 +755,7 @@ word [a-zA-Z0-9_]+
|
||||
/* Meta comments */
|
||||
|
||||
/* Converted from //{cmt}verilator ...{cmt} by preprocessor */
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SAX,PSL>{
|
||||
"/*verilator"{ws}*"*/" {} /* Ignore empty comments, may be `endif // verilator */
|
||||
"/*verilator clock_enable*/" { FL; return yVL_CLOCK_ENABLE; }
|
||||
"/*verilator coverage_block_off*/" { FL; return yVL_COVERAGE_BLOCK_OFF; }
|
||||
@@ -782,11 +790,11 @@ word [a-zA-Z0-9_]+
|
||||
/************************************************************************/
|
||||
|
||||
/* Single character operator thingies */
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SAX>{
|
||||
"{" { FL; return yytext[0]; }
|
||||
"}" { FL; return yytext[0]; }
|
||||
}
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SAX,PSL>{
|
||||
"!" { FL; return yytext[0]; }
|
||||
"#" { FL; return yytext[0]; }
|
||||
"$" { FL; return yytext[0]; }
|
||||
@@ -818,7 +826,7 @@ word [a-zA-Z0-9_]+
|
||||
/* Operators and multi-character symbols */
|
||||
|
||||
/* Verilog 1995 Operators */
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SAX,PSL>{
|
||||
"&&" { FL; return yP_ANDAND; }
|
||||
"||" { FL; return yP_OROR; }
|
||||
"<=" { FL; return yP_LTE; }
|
||||
@@ -840,7 +848,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* Verilog 2001 Operators */
|
||||
<V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
<V01,V05,VA5,S05,S09,S12,SAX,PSL>{
|
||||
"<<<" { FL; return yP_SLEFT; }
|
||||
">>>" { FL; return yP_SSRIGHT; }
|
||||
"**" { FL; return yP_POW; }
|
||||
@@ -850,7 +858,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* SystemVerilog Operators */
|
||||
<S05,S09,S12>{
|
||||
<S05,S09,S12,SAX>{
|
||||
"'" { FL; return yP_TICK; }
|
||||
"'{" { FL; return yP_TICKBRA; }
|
||||
"==?" { FL; return yP_WILDEQUAL; }
|
||||
@@ -899,7 +907,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* Identifiers and numbers */
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL,VLT>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SAX,PSL,VLT>{
|
||||
{escid} { FL; yylval.strp = PARSEP->newString
|
||||
(AstNode::encodeName(string(yytext+1))); // +1 to skip the backslash
|
||||
return yaID__LEX;
|
||||
@@ -912,24 +920,21 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
\" { yy_push_state(STRING); yymore(); }
|
||||
|
||||
[0-9]*?['']s?[bcodhBCODH][ \t\n]*[A-Fa-f0-9xXzZ_?]* {
|
||||
FL; LINECHECK(); yylval.nump = PARSEP->newNumber(yylval.fl,(char*)yytext);
|
||||
return yaINTNUM;
|
||||
}
|
||||
[0-9]*?['']s?[01xXzZ] { /* SystemVerilog */
|
||||
FL; yylval.nump = PARSEP->newNumber(yylval.fl,(char*)yytext);
|
||||
return yaINTNUM;
|
||||
}
|
||||
/* Note below is constructed to not match the ' that begins a '( or '{ */
|
||||
[0-9][_0-9]*[ \t\n]*['']s?[bcodhBCODH]?[ \t]*[A-Fa-f0-9xXzZ_?]+ {
|
||||
FL; LINECHECK(); yylval.nump = PARSEP->newNumber(yylval.fl,(char*)yytext);
|
||||
return yaINTNUM;
|
||||
}
|
||||
[0-9][_0-9]*[ \t\n]*['']s?[bcodhBCODH] {
|
||||
FL; LINECHECK(); yylval.nump = PARSEP->newNumber(yylval.fl,(char*)yytext);
|
||||
return yaINTNUM;
|
||||
}
|
||||
[0-9][_0-9]*[ \t\n]*['']s {
|
||||
{vnum} {
|
||||
/* "# 1'b0" is a delay value so must lex as "#" "1" "'b0" */
|
||||
if (PARSEP->prevLexToken()=='#') {
|
||||
int shortlen = 0;
|
||||
while (isdigit(yytext[shortlen])) shortlen++;
|
||||
if (shortlen) {
|
||||
// Push rest for later parse
|
||||
PARSEP->unputString(yytext+shortlen, yyleng-shortlen);
|
||||
FL; LINECHECKS(yytext,shortlen);
|
||||
// Return is stuff before '
|
||||
yytext[shortlen] = '\0';
|
||||
yylval.nump = PARSEP->newNumber(yylval.fl, (char*)yytext);
|
||||
return yaINTNUM;
|
||||
}
|
||||
}
|
||||
FL; LINECHECK(); yylval.nump = PARSEP->newNumber(yylval.fl,(char*)yytext);
|
||||
return yaINTNUM;
|
||||
}
|
||||
@@ -975,7 +980,7 @@ word [a-zA-Z0-9_]+
|
||||
/************************************************************************/
|
||||
/* Attributes */
|
||||
/* Note simulators vary in support for "(* /_*something*_/ foo*)" where _ doesn't exist */
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SAX>{
|
||||
"(*"({ws}|{crnl})*({id}|{escid}) { yymore(); yy_push_state(ATTRMODE); } // Doesn't match (*), but (* attr_spec
|
||||
}
|
||||
|
||||
@@ -992,7 +997,7 @@ word [a-zA-Z0-9_]+
|
||||
/* Preprocessor */
|
||||
/* Common for all SYSC header states */
|
||||
/* OPTIMIZE: we return one per line, make it one for the entire block */
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL,VLT,SYSCHDR,SYSCINT,SYSCIMP,SYSCIMPH,SYSCCTOR,SYSCDTOR,IGNORE>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SAX,PSL,VLT,SYSCHDR,SYSCINT,SYSCIMP,SYSCIMPH,SYSCCTOR,SYSCDTOR,IGNORE>{
|
||||
"`accelerate" { } // Verilog-XL compatibility
|
||||
"`autoexpand_vectornets" { } // Verilog-XL compatibility
|
||||
"`celldefine" { PARSEP->inCellDefine(true); }
|
||||
@@ -1037,7 +1042,7 @@ word [a-zA-Z0-9_]+
|
||||
"`begin_keywords"[ \t]*\"1800-2005\" { yy_push_state(S05); PARSEP->pushBeginKeywords(YY_START); }
|
||||
"`begin_keywords"[ \t]*\"1800-2009\" { yy_push_state(S09); PARSEP->pushBeginKeywords(YY_START); }
|
||||
"`begin_keywords"[ \t]*\"1800-2012\" { yy_push_state(S12); PARSEP->pushBeginKeywords(YY_START); }
|
||||
"`begin_keywords"[ \t]*\"1800+VAMS\" { yy_push_state(SA9); PARSEP->pushBeginKeywords(YY_START); }
|
||||
"`begin_keywords"[ \t]*\"1800[+]VAMS\" { yy_push_state(SAX); PARSEP->pushBeginKeywords(YY_START); } /*Latest SV*/
|
||||
"`end_keywords" { yy_pop_state(); if (!PARSEP->popBeginKeywords()) yyerrorf("`end_keywords when not inside `begin_keywords block"); }
|
||||
|
||||
/* Verilator */
|
||||
@@ -1068,7 +1073,7 @@ word [a-zA-Z0-9_]+
|
||||
/************************************************************************/
|
||||
/* Default rules - leave last */
|
||||
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL,VLT>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SAX,PSL,VLT>{
|
||||
"`"[a-zA-Z_0-9]+ { FL; yyerrorf("Define or directive not defined: %s",yytext); }
|
||||
"//"[^\n]* { } /* throw away single line comments */
|
||||
. { FL; return yytext[0]; } /* return single char ops. */
|
||||
@@ -1106,8 +1111,8 @@ int V3ParseImp::lexToken() {
|
||||
yylval = m_aheadVal;
|
||||
} else {
|
||||
// Parse new token
|
||||
token = yylexThis();
|
||||
//yylval // Set by yylexThis()
|
||||
token = yylexReadTok();
|
||||
//yylval // Set by yylexReadTok()
|
||||
}
|
||||
// If a paren, read another
|
||||
if (token == yCONST__LEX
|
||||
@@ -1116,7 +1121,7 @@ int V3ParseImp::lexToken() {
|
||||
) {
|
||||
if (debugFlex()) { cout<<" lexToken: reading ahead to find possible strength"<<endl; }
|
||||
V3ParseBisonYYSType curValue = yylval; // Remember value, as about to read ahead
|
||||
int nexttok = yylexThis();
|
||||
int nexttok = yylexReadTok();
|
||||
m_ahead = true;
|
||||
m_aheadToken = nexttok;
|
||||
m_aheadVal = yylval;
|
||||
|
||||
+168
-71
@@ -55,14 +55,16 @@ public:
|
||||
AstVarType m_varDecl; // Type for next signal declaration (reg/wire/etc)
|
||||
AstVarType m_varIO; // Type for next signal declaration (input/output/etc)
|
||||
AstVar* m_varAttrp; // Current variable for attribute adding
|
||||
AstRange* m_gateRangep; // Current range for gate declarations
|
||||
AstCase* m_caseAttrp; // Current case statement for attribute adding
|
||||
AstNodeDType* m_varDTypep; // Pointer to data type for next signal declaration
|
||||
AstNodeDType* m_memDTypep; // Pointer to data type for next member declaration
|
||||
int m_pinNum; // Pin number currently parsing
|
||||
string m_instModule; // Name of module referenced for instantiations
|
||||
AstPin* m_instParamp; // Parameters for instantiations
|
||||
AstNodeModule* m_modp; // Module
|
||||
int m_modTypeImpNum; // Implicit type number, incremented each module
|
||||
bool m_tracingParse; // Tracing disable for parser
|
||||
|
||||
static int s_modTypeImpNum; // Implicit type number, incremented each module
|
||||
|
||||
// CONSTRUCTORS
|
||||
V3ParseGrammar() {
|
||||
@@ -70,14 +72,14 @@ public:
|
||||
m_varDecl = AstVarType::UNKNOWN;
|
||||
m_varIO = AstVarType::UNKNOWN;
|
||||
m_varDTypep = NULL;
|
||||
m_gateRangep = NULL;
|
||||
m_memDTypep = NULL;
|
||||
m_pinNum = -1;
|
||||
m_instModule = "";
|
||||
m_instParamp = NULL;
|
||||
m_modp = NULL;
|
||||
m_modTypeImpNum = 0;
|
||||
m_varAttrp = NULL;
|
||||
m_caseAttrp = NULL;
|
||||
m_tracingParse = true;
|
||||
}
|
||||
static V3ParseGrammar* singletonp() {
|
||||
static V3ParseGrammar singleton;
|
||||
@@ -86,6 +88,9 @@ public:
|
||||
|
||||
// METHODS
|
||||
void argWrapList(AstNodeFTaskRef* nodep);
|
||||
bool allTracingOn(FileLine* fl) {
|
||||
return v3Global.opt.trace() && m_tracingParse && fl->tracingOn();
|
||||
}
|
||||
AstNodeDType* createArray(AstNodeDType* basep, AstRange* rangep, bool isPacked);
|
||||
AstVar* createVariable(FileLine* fileline, string name, AstRange* arrayp, AstNode* attrsp);
|
||||
AstNode* createSupplyExpr(FileLine* fileline, string name, int value);
|
||||
@@ -98,9 +103,15 @@ public:
|
||||
nodep->addNext(new AstStop(fileline));
|
||||
return nodep;
|
||||
}
|
||||
AstNode* createGatePin(AstNode* exprp) {
|
||||
AstRange* rangep = m_gateRangep;
|
||||
if (!rangep) return exprp;
|
||||
else return new AstGatePin(rangep->fileline(), exprp, rangep->cloneTree(true));
|
||||
}
|
||||
void endLabel(FileLine* fl, AstNode* nodep, string* endnamep) { endLabel(fl, nodep->prettyName(), endnamep); }
|
||||
void endLabel(FileLine* fl, string name, string* endnamep) {
|
||||
if (fl && endnamep && *endnamep != "" && name != *endnamep) {
|
||||
if (fl && endnamep && *endnamep != "" && name != *endnamep
|
||||
&& name != AstNode::prettyName(*endnamep)) {
|
||||
fl->v3warn(ENDLABEL,"End label '"<<*endnamep<<"' does not match begin label '"<<name<<"'");
|
||||
}
|
||||
}
|
||||
@@ -113,7 +124,6 @@ public:
|
||||
AstPackage* pkgp = SYMP->symRootp()->findIdFlat(AstPackage::dollarUnitName())->nodep()->castPackage();
|
||||
if (!pkgp) {
|
||||
pkgp = PARSEP->rootp()->dollarUnitPkgAddp();
|
||||
GRAMMARP->m_modp = pkgp; GRAMMARP->m_modTypeImpNum = 0;
|
||||
SYMP->reinsert(pkgp, SYMP->symRootp()); // Don't push/pop scope as they're global
|
||||
}
|
||||
return pkgp;
|
||||
@@ -159,6 +169,8 @@ public:
|
||||
const AstBasicDTypeKwd LOGIC = AstBasicDTypeKwd::LOGIC; // Shorthand "LOGIC"
|
||||
const AstBasicDTypeKwd LOGIC_IMPLICIT = AstBasicDTypeKwd::LOGIC_IMPLICIT;
|
||||
|
||||
int V3ParseGrammar::s_modTypeImpNum = 0;
|
||||
|
||||
//======================================================================
|
||||
// Macro functions
|
||||
|
||||
@@ -175,6 +187,8 @@ const AstBasicDTypeKwd LOGIC_IMPLICIT = AstBasicDTypeKwd::LOGIC_IMPLICIT;
|
||||
#define VARDONEP(portp,array,attrs) GRAMMARP->createVariable((portp)->fileline(),(portp)->name(),(array),(attrs))
|
||||
#define PINNUMINC() (GRAMMARP->m_pinNum++)
|
||||
|
||||
#define GATERANGE(rangep) { GRAMMARP->m_gateRangep = rangep; }
|
||||
|
||||
#define INSTPREP(modname,paramsp) { GRAMMARP->m_impliedDecl = true; GRAMMARP->m_instModule = modname; GRAMMARP->m_instParamp = paramsp; }
|
||||
|
||||
#define DEL(nodep) { if (nodep) nodep->deleteTree(); }
|
||||
@@ -676,7 +690,7 @@ timeunits_declaration<nodep>: // ==IEEE: timeunits_declaration
|
||||
|
||||
package_declaration: // ==IEEE: package_declaration
|
||||
packageFront package_itemListE yENDPACKAGE endLabelE
|
||||
{ $1->modTrace(v3Global.opt.trace() && $1->fileline()->tracingOn()); // Stash for implicit wires, etc
|
||||
{ $1->modTrace(GRAMMARP->allTracingOn($1->fileline())); // Stash for implicit wires, etc
|
||||
if ($2) $1->addStmtp($2);
|
||||
SYMP->popScope($1);
|
||||
GRAMMARP->endLabel($<fl>4,$1,$4); }
|
||||
@@ -686,8 +700,7 @@ packageFront<modulep>:
|
||||
yPACKAGE idAny ';'
|
||||
{ $$ = new AstPackage($1,*$2);
|
||||
$$->inLibrary(true); // packages are always libraries; don't want to make them a "top"
|
||||
$$->modTrace(v3Global.opt.trace());
|
||||
GRAMMARP->m_modp = $$; GRAMMARP->m_modTypeImpNum = 0;
|
||||
$$->modTrace(GRAMMARP->allTracingOn($$->fileline()));
|
||||
PARSEP->rootp()->addModulep($$);
|
||||
SYMP->pushNew($$); }
|
||||
;
|
||||
@@ -727,6 +740,11 @@ package_or_generate_item_declaration<nodep>: // ==IEEE: package_or_generate_item
|
||||
| ';' { $$ = NULL; }
|
||||
;
|
||||
|
||||
package_import_declarationList<nodep>:
|
||||
package_import_declaration { $$ = $1; }
|
||||
| package_import_declarationList package_import_declaration { $$ = $1->addNextNull($2); }
|
||||
;
|
||||
|
||||
package_import_declaration<nodep>: // ==IEEE: package_import_declaration
|
||||
yIMPORT package_import_itemList ';' { $$ = $2; }
|
||||
;
|
||||
@@ -753,9 +771,9 @@ package_import_itemObj<strp>: // IEEE: part of package_import_item
|
||||
module_declaration: // ==IEEE: module_declaration
|
||||
// // timeunits_declaration instead in module_item
|
||||
// // IEEE: module_nonansi_header + module_ansi_header
|
||||
modFront parameter_port_listE portsStarE ';'
|
||||
modFront importsAndParametersE portsStarE ';'
|
||||
module_itemListE yENDMODULE endLabelE
|
||||
{ $1->modTrace(v3Global.opt.trace() && $1->fileline()->tracingOn()); // Stash for implicit wires, etc
|
||||
{ $1->modTrace(GRAMMARP->allTracingOn($1->fileline())); // Stash for implicit wires, etc
|
||||
if ($2) $1->addStmtp($2); if ($3) $1->addStmtp($3);
|
||||
if ($5) $1->addStmtp($5);
|
||||
SYMP->popScope($1);
|
||||
@@ -765,6 +783,7 @@ module_declaration: // ==IEEE: module_declaration
|
||||
{ $1->modTrace(false); // Stash for implicit wires, etc
|
||||
if ($2) $1->addStmtp($2); if ($3) $1->addStmtp($3);
|
||||
if ($5) $1->addStmtp($5);
|
||||
GRAMMARP->m_tracingParse = true;
|
||||
SYMP->popScope($1);
|
||||
GRAMMARP->endLabel($<fl>7,$1,$7); }
|
||||
//
|
||||
@@ -777,19 +796,23 @@ modFront<modulep>:
|
||||
// // any formal arguments, as the arguments must land in the new scope.
|
||||
yMODULE lifetimeE idAny
|
||||
{ $$ = new AstModule($1,*$3); $$->inLibrary(PARSEP->inLibrary()||PARSEP->inCellDefine());
|
||||
$$->modTrace(v3Global.opt.trace());
|
||||
GRAMMARP->m_modp = $$; GRAMMARP->m_modTypeImpNum = 0;
|
||||
$$->modTrace(GRAMMARP->allTracingOn($$->fileline()));
|
||||
PARSEP->rootp()->addModulep($$);
|
||||
SYMP->pushNew($$); }
|
||||
;
|
||||
|
||||
importsAndParametersE<nodep>: // IEEE: common part of module_declaration, interface_declaration, program_declaration
|
||||
// // { package_import_declaration } [ parameter_port_list ]
|
||||
parameter_port_listE { $$ = $1; }
|
||||
| package_import_declarationList parameter_port_listE { $$ = $1->addNextNull($2); }
|
||||
;
|
||||
|
||||
udpFront<modulep>:
|
||||
yPRIMITIVE lifetimeE idAny
|
||||
{ $$ = new AstPrimitive($1,*$3); $$->inLibrary(true);
|
||||
$$->modTrace(false);
|
||||
$$->addStmtp(new AstPragma($1,AstPragmaType::INLINE_MODULE));
|
||||
PARSEP->fileline()->tracingOn(false);
|
||||
GRAMMARP->m_modp = $$; GRAMMARP->m_modTypeImpNum = 0;
|
||||
GRAMMARP->m_tracingParse = false;
|
||||
PARSEP->rootp()->addModulep($$);
|
||||
SYMP->pushNew($$); }
|
||||
;
|
||||
@@ -824,8 +847,7 @@ paramPortDeclOrArgList<nodep>: // IEEE: list_of_param_assignments + { parameter_
|
||||
|
||||
paramPortDeclOrArg<nodep>: // IEEE: param_assignment + parameter_port_declaration
|
||||
// // We combine the two as we can't tell which follows a comma
|
||||
param_assignment { $$ = $1; }
|
||||
| parameter_port_declarationFront param_assignment { $$ = $2; }
|
||||
parameter_port_declarationFrontE param_assignment { $$ = $2; }
|
||||
;
|
||||
|
||||
portsStarE<nodep>: // IEEE: .* + list_of_ports + list_of_port_declarations + empty
|
||||
@@ -995,7 +1017,7 @@ program_declaration: // IEEE: program_declaration + program_nonansi_header + pr
|
||||
// // timeunits_delcarationE is instead in program_item
|
||||
pgmFront parameter_port_listE portsStarE ';'
|
||||
program_itemListE yENDPROGRAM endLabelE
|
||||
{ $1->modTrace(v3Global.opt.trace() && $1->fileline()->tracingOn()); // Stash for implicit wires, etc
|
||||
{ $1->modTrace(GRAMMARP->allTracingOn($1->fileline())); // Stash for implicit wires, etc
|
||||
if ($2) $1->addStmtp($2); if ($3) $1->addStmtp($3);
|
||||
if ($5) $1->addStmtp($5);
|
||||
SYMP->popScope($1);
|
||||
@@ -1007,8 +1029,7 @@ program_declaration: // IEEE: program_declaration + program_nonansi_header + pr
|
||||
pgmFront<modulep>:
|
||||
yPROGRAM lifetimeE idAny/*new_program*/
|
||||
{ $$ = new AstModule($1,*$3); $$->inLibrary(PARSEP->inLibrary()||PARSEP->inCellDefine());
|
||||
$$->modTrace(v3Global.opt.trace());
|
||||
GRAMMARP->m_modp = $$; GRAMMARP->m_modTypeImpNum = 0;
|
||||
$$->modTrace(GRAMMARP->allTracingOn($$->fileline()));
|
||||
PARSEP->rootp()->addModulep($$);
|
||||
SYMP->pushNew($$); }
|
||||
;
|
||||
@@ -1133,10 +1154,13 @@ parameter_declarationFront: // IEEE: parameter_declaration w/o assignment
|
||||
//UNSUP varGParamReset yTYPE { /*VARRESET-in-varGParam*/ VARDTYPE($2); }
|
||||
;
|
||||
|
||||
parameter_port_declarationFront: // IEEE: parameter_port_declaration w/o assignment
|
||||
parameter_port_declarationFrontE: // IEEE: parameter_port_declaration w/o assignment
|
||||
// // IEEE: parameter_declaration (minus assignment)
|
||||
parameter_declarationFront { }
|
||||
//
|
||||
varGParamReset implicit_typeE { /*VARRESET-in-varGParam*/ VARDTYPE($2); }
|
||||
| varGParamReset data_type { /*VARRESET-in-varGParam*/ VARDTYPE($2); }
|
||||
| implicit_typeE { /*VARRESET-in-varGParam*/ VARDTYPE($1); }
|
||||
| data_type { /*VARRESET-in-varGParam*/ VARDTYPE($1); }
|
||||
//UNSUP varGParamReset yTYPE { /*VARRESET-in-varGParam*/ VARDTYPE($2); }
|
||||
//UNSUP data_type { VARDTYPE($1); }
|
||||
//UNSUP yTYPE { VARDTYPE($1); }
|
||||
;
|
||||
@@ -1323,10 +1347,10 @@ data_typeBasic<dtypep>: // IEEE: part of data_type
|
||||
|
||||
data_typeNoRef<dtypep>: // ==IEEE: data_type, excluding class_type etc references
|
||||
data_typeBasic { $$ = $1; }
|
||||
| struct_unionDecl packed_dimensionListE { $$ = GRAMMARP->createArray(new AstDefImplicitDType($1->fileline(),"__typeimpsu"+cvtToStr(GRAMMARP->m_modTypeImpNum++),
|
||||
GRAMMARP->m_modp,VFlagChildDType(),$1),$2,true); }
|
||||
| enumDecl { $$ = new AstDefImplicitDType($1->fileline(),"__typeimpenum"+cvtToStr(GRAMMARP->m_modTypeImpNum++),
|
||||
GRAMMARP->m_modp,VFlagChildDType(),$1); }
|
||||
| struct_unionDecl packed_dimensionListE { $$ = GRAMMARP->createArray(new AstDefImplicitDType($1->fileline(),"__typeimpsu"+cvtToStr(GRAMMARP->s_modTypeImpNum++),
|
||||
SYMP,VFlagChildDType(),$1),$2,true); }
|
||||
| enumDecl { $$ = new AstDefImplicitDType($1->fileline(),"__typeimpenum"+cvtToStr(GRAMMARP->s_modTypeImpNum++),
|
||||
SYMP,VFlagChildDType(),$1); }
|
||||
| ySTRING { $$ = new AstBasicDType($1,AstBasicDTypeKwd::STRING); }
|
||||
| yCHANDLE { $$ = new AstBasicDType($1,AstBasicDTypeKwd::CHANDLE); }
|
||||
//UNSUP yEVENT { UNSUP }
|
||||
@@ -1576,8 +1600,9 @@ implicit_typeE<dtypep>: // IEEE: part of *data_type_or_implicit
|
||||
|
||||
type_declaration<nodep>: // ==IEEE: type_declaration
|
||||
// // Use idAny, as we can redeclare a typedef on an existing typedef
|
||||
yTYPEDEF data_type idAny variable_dimensionListE ';' { $$ = new AstTypedef($<fl>1, *$3, VFlagChildDType(), GRAMMARP->createArray($2,$4,false));
|
||||
SYMP->reinsert($$); }
|
||||
yTYPEDEF data_type idAny variable_dimensionListE dtypeAttrListE ';'
|
||||
/**/ { $$ = new AstTypedef($<fl>1, *$3, $5, VFlagChildDType(), GRAMMARP->createArray($2,$4,false));
|
||||
SYMP->reinsert($$); }
|
||||
//UNSUP yTYPEDEF id/*interface*/ '.' idAny/*type*/ idAny/*type*/ ';' { $$ = NULL; $1->v3error("Unsupported: SystemVerilog 2005 typedef in this context"); } //UNSUP
|
||||
// // Combines into above "data_type id" rule
|
||||
// // Verilator: Not important what it is in the AST, just need to make sure the yaID__aTYPE gets returned
|
||||
@@ -1588,6 +1613,20 @@ type_declaration<nodep>: // ==IEEE: type_declaration
|
||||
//UNSUP yTYPEDEF yCLASS idAny ';' { $$ = NULL; $$ = new AstTypedefFwd($<fl>1, *$3); SYMP->reinsert($$); }
|
||||
;
|
||||
|
||||
dtypeAttrListE<nodep>:
|
||||
/* empty */ { $$ = NULL; }
|
||||
| dtypeAttrList { $$ = $1; }
|
||||
;
|
||||
|
||||
dtypeAttrList<nodep>:
|
||||
dtypeAttr { $$ = $1; }
|
||||
| dtypeAttrList dtypeAttr { $$ = $1->addNextNull($2); }
|
||||
;
|
||||
|
||||
dtypeAttr<nodep>:
|
||||
yVL_PUBLIC { $$ = new AstAttrOf($1,AstAttrType::DT_PUBLIC); }
|
||||
;
|
||||
|
||||
//************************************************
|
||||
// Module Items
|
||||
|
||||
@@ -1892,10 +1931,6 @@ netId<strp>:
|
||||
| idSVKwd { $$ = $1; $<fl>$=$<fl>1; }
|
||||
;
|
||||
|
||||
sigId<varp>:
|
||||
id { $$ = VARDONEA($<fl>1,*$1, NULL, NULL); }
|
||||
;
|
||||
|
||||
sigAttrListE<nodep>:
|
||||
/* empty */ { $$ = NULL; }
|
||||
| sigAttrList { $$ = $1; }
|
||||
@@ -1913,7 +1948,7 @@ sigAttr<nodep>:
|
||||
| yVL_PUBLIC_FLAT { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT); v3Global.dpi(true); }
|
||||
| yVL_PUBLIC_FLAT_RD { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT_RD); v3Global.dpi(true); }
|
||||
| yVL_PUBLIC_FLAT_RW { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT_RW); v3Global.dpi(true); }
|
||||
| yVL_PUBLIC_FLAT_RW attr_event_control { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT_RW); v3Global.dpi(true);
|
||||
| yVL_PUBLIC_FLAT_RW attr_event_control { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT_RW); v3Global.dpi(true);
|
||||
$$ = $$->addNext(new AstAlwaysPublic($1,$2,NULL)); }
|
||||
| yVL_ISOLATE_ASSIGNMENTS { $$ = new AstAttrOf($1,AstAttrType::VAR_ISOLATE_ASSIGNMENTS); }
|
||||
| yVL_SC_BV { $$ = new AstAttrOf($1,AstAttrType::VAR_SC_BV); }
|
||||
@@ -1958,7 +1993,8 @@ packed_dimension<rangep>: // ==IEEE: packed_dimension
|
||||
param_assignment<varp>: // ==IEEE: param_assignment
|
||||
// // IEEE: constant_param_expression
|
||||
// // constant_param_expression: '$' is in expr
|
||||
sigId sigAttrListE '=' expr { $$ = $1; $1->addAttrsp($2); $$->valuep($4); }
|
||||
id/*new-parameter*/ variable_dimensionListE sigAttrListE '=' expr
|
||||
/**/ { $$ = VARDONEA($<fl>1,*$1, $2, $3); $$->valuep($5); }
|
||||
//UNSUP: exprOrDataType instead of expr
|
||||
;
|
||||
|
||||
@@ -2002,9 +2038,11 @@ instnameList<nodep>:
|
||||
| instnameList ',' instnameParen { $$ = $1->addNext($3); }
|
||||
;
|
||||
|
||||
instnameParen<nodep>:
|
||||
id instRangeE '(' cellpinList ')' { $$ = new AstCell($<fl>1,*$1,GRAMMARP->m_instModule,$4, GRAMMARP->m_instParamp,$2); }
|
||||
| id instRangeE { $$ = new AstCell($<fl>1,*$1,GRAMMARP->m_instModule,NULL,GRAMMARP->m_instParamp,$2); }
|
||||
instnameParen<cellp>:
|
||||
id instRangeE '(' cellpinList ')' { $$ = new AstCell($<fl>1,*$1,GRAMMARP->m_instModule,$4, GRAMMARP->m_instParamp,$2);
|
||||
$$->trace(GRAMMARP->allTracingOn($<fl>1)); }
|
||||
| id instRangeE { $$ = new AstCell($<fl>1,*$1,GRAMMARP->m_instModule,NULL,GRAMMARP->m_instParamp,$2);
|
||||
$$->trace(GRAMMARP->allTracingOn($<fl>1)); }
|
||||
//UNSUP instRangeE '(' cellpinList ')' { UNSUP } // UDP
|
||||
// // Adding above and switching to the Verilog-Perl syntax
|
||||
// // causes a shift conflict due to use of idClassSel inside exprScope.
|
||||
@@ -2088,6 +2126,7 @@ senitem<senitemp>: // IEEE: part of event_expression, non-'OR' ',' terms
|
||||
| senitemVar { $$ = $1; }
|
||||
| '(' senitemVar ')' { $$ = $2; }
|
||||
//UNSUP expr { UNSUP }
|
||||
| '{' event_expression '}' { $$ = $2; }
|
||||
//UNSUP expr yIFF expr { UNSUP }
|
||||
// Since expr is unsupported we allow and ignore constants (removed in V3Const)
|
||||
| yaINTNUM { $$ = NULL; }
|
||||
@@ -2190,7 +2229,12 @@ statement_item<nodep>: // IEEE: statement_item
|
||||
if ($1 == uniq_UNIQUE0) $2->unique0Pragma(true);
|
||||
if ($1 == uniq_PRIORITY) $2->priorityPragma(true); }
|
||||
//UNSUP caseStart caseAttrE yMATCHES case_patternListE yENDCASE { }
|
||||
//UNSUP caseStart caseAttrE yINSIDE case_insideListE yENDCASE { }
|
||||
| unique_priorityE caseStart caseAttrE yINSIDE case_insideListE yENDCASE { $$ = $2; if ($5) $2->addItemsp($5);
|
||||
if (!$2->caseSimple()) $2->v3error("Illegal to have inside on a casex/casez");
|
||||
$2->caseInsideSet();
|
||||
if ($1 == uniq_UNIQUE) $2->uniquePragma(true);
|
||||
if ($1 == uniq_UNIQUE0) $2->unique0Pragma(true);
|
||||
if ($1 == uniq_PRIORITY) $2->priorityPragma(true); }
|
||||
//
|
||||
// // IEEE: conditional_statement
|
||||
| unique_priorityE yIF '(' expr ')' stmtBlock %prec prLOWER_THAN_ELSE
|
||||
@@ -2328,9 +2372,9 @@ unique_priorityE<uniqstate>: // IEEE: unique_priority + empty
|
||||
;
|
||||
|
||||
caseStart<casep>: // IEEE: part of case_statement
|
||||
yCASE '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,AstCaseType::CT_CASE,$3,NULL); }
|
||||
| yCASEX '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,AstCaseType::CT_CASEX,$3,NULL); }
|
||||
| yCASEZ '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,AstCaseType::CT_CASEZ,$3,NULL); }
|
||||
yCASE '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,VCaseType::CT_CASE,$3,NULL); }
|
||||
| yCASEX '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,VCaseType::CT_CASEX,$3,NULL); }
|
||||
| yCASEZ '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,VCaseType::CT_CASEZ,$3,NULL); }
|
||||
;
|
||||
|
||||
caseAttrE:
|
||||
@@ -2344,6 +2388,11 @@ case_itemListE<caseitemp>: // IEEE: [ { case_item } ]
|
||||
| case_itemList { $$ = $1; }
|
||||
;
|
||||
|
||||
case_insideListE<caseitemp>: // IEEE: [ { case_inside_item } ]
|
||||
/* empty */ { $$ = NULL; }
|
||||
| case_inside_itemList { $$ = $1; }
|
||||
;
|
||||
|
||||
case_itemList<caseitemp>: // IEEE: { case_item + ... }
|
||||
caseCondList ':' stmtBlock { $$ = new AstCaseItem($2,$1,$3); }
|
||||
| yDEFAULT ':' stmtBlock { $$ = new AstCaseItem($2,NULL,$3); }
|
||||
@@ -2353,6 +2402,15 @@ case_itemList<caseitemp>: // IEEE: { case_item + ... }
|
||||
| case_itemList yDEFAULT ':' stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,NULL,$4)); }
|
||||
;
|
||||
|
||||
case_inside_itemList<caseitemp>: // IEEE: { case_inside_item + open_range_list ... }
|
||||
open_range_list ':' stmtBlock { $$ = new AstCaseItem($2,$1,$3); }
|
||||
| yDEFAULT ':' stmtBlock { $$ = new AstCaseItem($2,NULL,$3); }
|
||||
| yDEFAULT stmtBlock { $$ = new AstCaseItem($1,NULL,$2); }
|
||||
| case_inside_itemList open_range_list ':' stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,$2,$4)); }
|
||||
| case_inside_itemList yDEFAULT stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($2,NULL,$3)); }
|
||||
| case_inside_itemList yDEFAULT ':' stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,NULL,$4)); }
|
||||
;
|
||||
|
||||
open_range_list<nodep>: // ==IEEE: open_range_list + open_value_range
|
||||
open_value_range { $$ = $1; }
|
||||
| open_range_list ',' open_value_range { $$ = $1;$1->addNext($3); }
|
||||
@@ -2829,8 +2887,8 @@ expr<nodep>: // IEEE: part of expression/constant_expression/primary
|
||||
| '~' ~r~expr %prec prNEGATION { $$ = new AstNot ($1,$2); }
|
||||
| '|' ~r~expr %prec prREDUCTION { $$ = new AstRedOr ($1,$2); }
|
||||
| '^' ~r~expr %prec prREDUCTION { $$ = new AstRedXor ($1,$2); }
|
||||
| yP_NAND ~r~expr %prec prREDUCTION { $$ = new AstNot($1,new AstRedAnd($1,$2)); }
|
||||
| yP_NOR ~r~expr %prec prREDUCTION { $$ = new AstNot($1,new AstRedOr ($1,$2)); }
|
||||
| yP_NAND ~r~expr %prec prREDUCTION { $$ = new AstLogNot($1,new AstRedAnd($1,$2)); }
|
||||
| yP_NOR ~r~expr %prec prREDUCTION { $$ = new AstLogNot($1,new AstRedOr ($1,$2)); }
|
||||
| yP_XNOR ~r~expr %prec prREDUCTION { $$ = new AstRedXnor ($1,$2); }
|
||||
//
|
||||
// // IEEE: inc_or_dec_expression
|
||||
@@ -2986,7 +3044,8 @@ exprNoStr<nodep>: // expression with string removed
|
||||
exprOkLvalue<nodep>: // expression that's also OK to use as a variable_lvalue
|
||||
~l~exprScope { $$ = $1; }
|
||||
// // IEEE: concatenation/constant_concatenation
|
||||
| '{' cateList '}' { $$ = $2; }
|
||||
// // Replicate(1) required as otherwise "{a}" would not be self-determined
|
||||
| '{' cateList '}' { $$ = new AstReplicate($1,$2,1); }
|
||||
// // IEEE: assignment_pattern_expression
|
||||
// // IEEE: [ assignment_pattern_expression_type ] == [ ps_type_id /ps_paremeter_id/data_type]
|
||||
// // We allow more here than the spec requires
|
||||
@@ -2994,7 +3053,7 @@ exprOkLvalue<nodep>: // expression that's also OK to use as a variable_lvalue
|
||||
| data_type assignment_pattern { $$ = $2; $2->childDTypep($1); }
|
||||
| assignment_pattern { $$ = $1; }
|
||||
//
|
||||
//UNSUP streaming_concatenation { UNSUP }
|
||||
| streaming_concatenation { $$ = $1; }
|
||||
;
|
||||
|
||||
fexprOkLvalue<nodep>: // exprOkLValue, For use as first part of statement (disambiguates <=)
|
||||
@@ -3096,6 +3155,32 @@ argsDotted<nodep>: // IEEE: part of list_of_arguments
|
||||
| '.' idAny '(' expr ')' { $$ = new AstArg($1,*$2,$4); }
|
||||
;
|
||||
|
||||
streaming_concatenation<nodep>: // ==IEEE: streaming_concatenation
|
||||
// // Need to disambiguate {<< expr-{ ... expr-} stream_concat }
|
||||
// // From {<< stream-{ ... stream-} }
|
||||
// // Likewise simple_type's idScoped from constExpr's idScope
|
||||
// // Thus we allow always any two operations. Sorry
|
||||
// // IEEE: "'{' yP_SL/R stream_concatenation '}'"
|
||||
// // IEEE: "'{' yP_SL/R simple_type stream_concatenation '}'"
|
||||
// // IEEE: "'{' yP_SL/R constExpr stream_concatenation '}'"
|
||||
'{' yP_SLEFT stream_concOrExprOrType '}' { $$ = new AstStreamL($1, $3, new AstConst($1,1)); }
|
||||
| '{' yP_SRIGHT stream_concOrExprOrType '}' { $$ = new AstStreamR($1, $3, new AstConst($1,1)); }
|
||||
| '{' yP_SLEFT stream_concOrExprOrType stream_concatenation '}' { $$ = new AstStreamL($1, $4, $3); }
|
||||
| '{' yP_SRIGHT stream_concOrExprOrType stream_concatenation '}' { $$ = new AstStreamR($1, $4, $3); }
|
||||
;
|
||||
|
||||
stream_concOrExprOrType<nodep>: // IEEE: stream_concatenation | slice_size:simple_type | slice_size:constExpr
|
||||
cateList { $$ = $1; }
|
||||
| simple_type { $$ = $1; }
|
||||
// // stream_concatenation found via cateList:stream_expr:'{-normal-concat'
|
||||
// // simple_typeRef found via cateList:stream_expr:expr:id
|
||||
// // constant_expression found via cateList:stream_expr:expr
|
||||
;
|
||||
|
||||
stream_concatenation<nodep>: // ==IEEE: stream_concatenation
|
||||
'{' cateList '}' { $$ = $2; }
|
||||
;
|
||||
|
||||
stream_expression<nodep>: // ==IEEE: stream_expression
|
||||
// // IEEE: array_range_expression expanded below
|
||||
expr { $$ = $1; }
|
||||
@@ -3199,62 +3284,66 @@ gateUnsupList<nodep>:
|
||||
| gateUnsupList ',' gateUnsup { $$ = $1->addNext($3); }
|
||||
;
|
||||
|
||||
gateRangeE<nodep>:
|
||||
instRangeE { $$ = $1; GATERANGE($1); }
|
||||
;
|
||||
|
||||
gateBuf<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' expr ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gatePinExpr ')'
|
||||
{ $$ = new AstAssignW ($3,$4,$6); DEL($2); }
|
||||
;
|
||||
gateBufif0<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' expr ',' expr ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gatePinExpr ',' gatePinExpr ')'
|
||||
{ $$ = new AstAssignW ($3,$4,new AstBufIf1($3,new AstNot($3,$8),$6)); DEL($2); }
|
||||
;
|
||||
gateBufif1<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' expr ',' expr ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gatePinExpr ',' gatePinExpr ')'
|
||||
{ $$ = new AstAssignW ($3,$4,new AstBufIf1($3,$8,$6)); DEL($2); }
|
||||
;
|
||||
gateNot<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' expr ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gatePinExpr ')'
|
||||
{ $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); DEL($2); }
|
||||
;
|
||||
gateNotif0<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' expr ',' expr ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gatePinExpr ',' gatePinExpr ')'
|
||||
{ $$ = new AstAssignW ($3,$4,new AstBufIf1($3,new AstNot($3,$8), new AstNot($3, $6))); DEL($2); }
|
||||
;
|
||||
gateNotif1<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' expr ',' expr ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gatePinExpr ',' gatePinExpr ')'
|
||||
{ $$ = new AstAssignW ($3,$4,new AstBufIf1($3,$8, new AstNot($3,$6))); DEL($2); }
|
||||
;
|
||||
gateAnd<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' gateAndPinList ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gateAndPinList ')'
|
||||
{ $$ = new AstAssignW ($3,$4,$6); DEL($2); }
|
||||
;
|
||||
gateNand<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' gateAndPinList ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gateAndPinList ')'
|
||||
{ $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); DEL($2); }
|
||||
;
|
||||
gateOr<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' gateOrPinList ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gateOrPinList ')'
|
||||
{ $$ = new AstAssignW ($3,$4,$6); DEL($2); }
|
||||
;
|
||||
gateNor<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' gateOrPinList ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gateOrPinList ')'
|
||||
{ $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); DEL($2); }
|
||||
;
|
||||
gateXor<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' gateXorPinList ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gateXorPinList ')'
|
||||
{ $$ = new AstAssignW ($3,$4,$6); DEL($2); }
|
||||
;
|
||||
gateXnor<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ',' gateXorPinList ')'
|
||||
gateIdE gateRangeE '(' variable_lvalue ',' gateXorPinList ')'
|
||||
{ $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); DEL($2); }
|
||||
;
|
||||
gatePullup<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ')' { $$ = new AstPull ($3, $4, true); DEL($2); }
|
||||
gateIdE gateRangeE '(' variable_lvalue ')' { $$ = new AstPull ($3, $4, true); DEL($2); }
|
||||
;
|
||||
gatePulldown<nodep>:
|
||||
gateIdE instRangeE '(' variable_lvalue ')' { $$ = new AstPull ($3, $4, false); DEL($2); }
|
||||
gateIdE gateRangeE '(' variable_lvalue ')' { $$ = new AstPull ($3, $4, false); DEL($2); }
|
||||
;
|
||||
gateUnsup<nodep>:
|
||||
gateIdE instRangeE '(' gateUnsupPinList ')' { $$ = new AstImplicit ($3,$4); DEL($2); }
|
||||
gateIdE gateRangeE '(' gateUnsupPinList ')' { $$ = new AstImplicit ($3,$4); DEL($2); }
|
||||
;
|
||||
|
||||
gateIdE:
|
||||
@@ -3263,20 +3352,24 @@ gateIdE:
|
||||
;
|
||||
|
||||
gateAndPinList<nodep>:
|
||||
expr { $$ = $1; }
|
||||
| gateAndPinList ',' expr { $$ = new AstAnd($2,$1,$3); }
|
||||
gatePinExpr { $$ = $1; }
|
||||
| gateAndPinList ',' gatePinExpr { $$ = new AstAnd($2,$1,$3); }
|
||||
;
|
||||
gateOrPinList<nodep>:
|
||||
expr { $$ = $1; }
|
||||
| gateOrPinList ',' expr { $$ = new AstOr($2,$1,$3); }
|
||||
gatePinExpr { $$ = $1; }
|
||||
| gateOrPinList ',' gatePinExpr { $$ = new AstOr($2,$1,$3); }
|
||||
;
|
||||
gateXorPinList<nodep>:
|
||||
expr { $$ = $1; }
|
||||
| gateXorPinList ',' expr { $$ = new AstXor($2,$1,$3); }
|
||||
gatePinExpr { $$ = $1; }
|
||||
| gateXorPinList ',' gatePinExpr { $$ = new AstXor($2,$1,$3); }
|
||||
;
|
||||
gateUnsupPinList<nodep>:
|
||||
expr { $$ = $1; }
|
||||
| gateUnsupPinList ',' expr { $$ = $1->addNext($3); }
|
||||
gatePinExpr { $$ = $1; }
|
||||
| gateUnsupPinList ',' gatePinExpr { $$ = $1->addNext($3); }
|
||||
;
|
||||
|
||||
gatePinExpr<nodep>:
|
||||
expr { $$ = GRAMMARP ->createGatePin($1); }
|
||||
;
|
||||
|
||||
strengthSpecE: // IEEE: drive_strength + pullup_strength + pulldown_strength + charge_strength - plus empty
|
||||
@@ -3365,6 +3458,7 @@ variable_lvalue<nodep>: // IEEE: variable_lvalue or net_lvalue
|
||||
//UNSUP idClassSel yP_TICKBRA variable_lvalueList '}' { UNSUP }
|
||||
//UNSUP /**/ yP_TICKBRA variable_lvalueList '}' { UNSUP }
|
||||
//UNSUP streaming_concatenation { UNSUP }
|
||||
| streaming_concatenation { $$ = $1; }
|
||||
;
|
||||
|
||||
variable_lvalueConcList<nodep>: // IEEE: part of variable_lvalue: '{' variable_lvalue { ',' variable_lvalue } '}'
|
||||
@@ -3628,6 +3722,8 @@ void V3ParseGrammar::argWrapList(AstNodeFTaskRef* nodep) {
|
||||
AstNode* outp = NULL;
|
||||
while (nodep->pinsp()) {
|
||||
AstNode* exprp = nodep->pinsp()->unlinkFrBack();
|
||||
// addNext can handle nulls:
|
||||
// cppcheck-suppress nullPointer
|
||||
outp = outp->addNext(new AstArg(exprp->fileline(), "", exprp));
|
||||
}
|
||||
if (outp) nodep->addPinsp(outp);
|
||||
@@ -3708,8 +3804,9 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, string name, AstRange
|
||||
// We need to autosize parameters and integers separately
|
||||
//
|
||||
// Propagate from current module tracing state
|
||||
if (nodep->isGenVar() || nodep->isParam()) nodep->trace(false);
|
||||
else nodep->trace(v3Global.opt.trace() && nodep->fileline()->tracingOn());
|
||||
if (nodep->isGenVar()) nodep->trace(false);
|
||||
else if (nodep->isParam() && !v3Global.opt.traceParams()) nodep->trace(false);
|
||||
else nodep->trace(allTracingOn(nodep->fileline()));
|
||||
|
||||
// Remember the last variable created, so we can attach attributes to it in later parsing
|
||||
GRAMMARP->m_varAttrp = nodep;
|
||||
|
||||
+25
-3
@@ -61,6 +61,7 @@ my $opt_vlt;
|
||||
my $opt_vcs;
|
||||
my $opt_verbose;
|
||||
my $Opt_Verilated_Debug;
|
||||
our $Opt_Unsupported;
|
||||
our $Opt_Verilation = 1;
|
||||
our @Opt_Driver_Verilator_Flags;
|
||||
|
||||
@@ -83,6 +84,7 @@ if (! GetOptions (
|
||||
"site!" => \$opt_site,
|
||||
"stop!" => \$opt_stop,
|
||||
"trace!" => \$opt_trace,
|
||||
"unsupported!"=> \$Opt_Unsupported,
|
||||
"v3!" => \$opt_vlt, # Old
|
||||
"vl!" => \$opt_vlt, # Old
|
||||
"vlt!" => \$opt_vlt,
|
||||
@@ -333,7 +335,11 @@ sub new {
|
||||
v_flags2 => [], # Overridden in some sim files
|
||||
v_other_filenames => [], # After the filename so we can spec multiple files
|
||||
all_run_flags => [],
|
||||
pli_flags => ["-I$ENV{VERILATOR_ROOT}/include/vltstd -fPIC -export-dynamic -shared -o $self->{obj_dir}/libvpi.so"],
|
||||
pli_flags => ["-I$ENV{VERILATOR_ROOT}/include/vltstd -fPIC -shared"
|
||||
.(($^O eq "darwin" )
|
||||
? " -Wl,-undefined,dynamic_lookup"
|
||||
: " -export-dynamic")
|
||||
." -o $self->{obj_dir}/libvpi.so"],
|
||||
# ATSIM
|
||||
atsim => 0,
|
||||
atsim_flags => [split(/\s+/,"-c +sv +define+ATSIM"),
|
||||
@@ -430,7 +436,9 @@ sub unsupported {
|
||||
my $self = shift;
|
||||
my $msg = join('',@_);
|
||||
warn "%Unsupported: $self->{mode}/$self->{name}: ".$msg."\n";
|
||||
$self->{unsupporteds} ||= "Unsupported: ".$msg;
|
||||
if (!$::Opt_Unsupported) {
|
||||
$self->{unsupporteds} ||= "Unsupported: ".$msg;
|
||||
}
|
||||
}
|
||||
|
||||
sub prep {
|
||||
@@ -665,7 +673,7 @@ sub compile {
|
||||
|
||||
if ($param{make_pli}) {
|
||||
$self->oprint("Compile vpi\n");
|
||||
my @cmd = ('g++', @{$param{pli_flags}}, "-DIS_VPI", "$self->{t_dir}/$self->{name}.cpp");
|
||||
my @cmd = ('c++', @{$param{pli_flags}}, "-DIS_VPI", "$self->{t_dir}/$self->{name}.cpp");
|
||||
|
||||
$self->_run(logfile=>"$self->{obj_dir}/pli_compile.log",
|
||||
fails=>$param{fails},
|
||||
@@ -817,6 +825,11 @@ sub ok {
|
||||
return $self->{ok};
|
||||
}
|
||||
|
||||
sub continuing {
|
||||
my $self = (ref $_[0]? shift : $Self);
|
||||
return !($self->errors || $self->skips || $self->unsupporteds);
|
||||
}
|
||||
|
||||
sub errors {
|
||||
my $self = (ref $_[0]? shift : $Self);
|
||||
return $self->{errors};
|
||||
@@ -1416,6 +1429,11 @@ sub vcd_identical {
|
||||
if ($out ne '') {
|
||||
print $out;
|
||||
$self->error("VCD miscompare $fn1 $fn2\n");
|
||||
if ($ENV{HARNESS_UPDATE_GOLDEN}) { # Update golden files with current
|
||||
warn "%Warning: HARNESS_UPDATE_GOLDEN set: cp $fn1 $fn2\n";
|
||||
eval "use File::Copy;";
|
||||
File::Copy::copy($fn1,$fn2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1843,6 +1861,10 @@ Stop on the first error.
|
||||
|
||||
Set the simulator specific flags to request waveform tracing.
|
||||
|
||||
=item --unsupported
|
||||
|
||||
Run tests even if marked as unsupported.
|
||||
|
||||
=item --vcs
|
||||
|
||||
Run using Synopsys VCS simulator.
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// please note it here, otherwise:**
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2013 by ____YOUR_NAME_HERE____.
|
||||
// without warranty, 2014 by ____YOUR_NAME_HERE____.
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
@@ -54,10 +54,10 @@ module t (/*AUTOARG*/
|
||||
if (cyc==0) begin
|
||||
// Setup
|
||||
crc <= 64'h5aef0c8d_d70a4497;
|
||||
sum <= 64'h0;
|
||||
sum <= '0;
|
||||
end
|
||||
else if (cyc<10) begin
|
||||
sum <= 64'h0;
|
||||
sum <= '0;
|
||||
end
|
||||
else if (cyc<90) begin
|
||||
end
|
||||
|
||||
@@ -11,7 +11,7 @@ module t (/*AUTOARG*/
|
||||
input clk;
|
||||
integer cyc; initial cyc=1;
|
||||
|
||||
reg [31:0] a, b, c, d, e, f, g;
|
||||
reg [31:0] a, b, c, d, e, f, g, h;
|
||||
|
||||
always @ (*) begin // Test Verilog 2001 (*)
|
||||
// verilator lint_off COMBDLY
|
||||
@@ -33,6 +33,9 @@ module t (/*AUTOARG*/
|
||||
always @ (1'b0, CONSTANT, f) begin // not technically legal, see bug412
|
||||
g = f;
|
||||
end
|
||||
always @ ({CONSTANT, g}) begin // bug745
|
||||
h = g;
|
||||
end
|
||||
//always @ ((posedge b) or (a or b)) begin // note both illegal
|
||||
|
||||
always @ (posedge clk) begin
|
||||
@@ -46,7 +49,7 @@ module t (/*AUTOARG*/
|
||||
if (c != 32'hfeedface) $stop;
|
||||
end
|
||||
if (cyc==3) begin
|
||||
if (g != 32'hfeedface) $stop;
|
||||
if (h != 32'hfeedface) $stop;
|
||||
end
|
||||
if (cyc==7) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
|
||||
@@ -7,8 +7,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug355");
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
|
||||
@@ -12,7 +12,12 @@ module t (/*AUTOARG*/
|
||||
|
||||
logic [1:0] [3:0] [3:0] array_simp; // big endian array
|
||||
|
||||
logic [3:0] array_oned;
|
||||
|
||||
initial begin
|
||||
array_oned = '{2:1'b1, 0:1'b1, default:1'b0};
|
||||
if (array_oned != 4'b0101) $stop;
|
||||
|
||||
array_simp[0] = '{ 4'd3, 4'd2, 4'd1, 4'd0};
|
||||
if (array_simp[0] !== 16'h3210) $stop;
|
||||
|
||||
@@ -26,11 +31,21 @@ module t (/*AUTOARG*/
|
||||
//array_simp[0] = '{ 1:4'd3, default:13};
|
||||
//if (array_simp[0] !== 16'hDD3D) $stop;
|
||||
|
||||
array_simp = '{ '{ 4'd3, 4'd2, 4'd1, 4'd0 }, '{ 4'd1, 4'd2, 4'd3, 4'd4 }};
|
||||
array_simp = '{ '{ 4'd3, 4'd2, 4'd1, 4'd0 }, '{ 4'd1, 4'd2, 4'd3, 4'd4 }};
|
||||
if (array_simp !== 32'h3210_1234) $stop;
|
||||
|
||||
// IEEE says '{} allowed only on assignments, not !=, ==.
|
||||
|
||||
// Doesn't seem to work for unpacked arrays in other simulators
|
||||
//array_simp <= '{2 { '{4 { 4'd3, 4'd2, 4'd1, 4'd0 }} } };
|
||||
array_simp = '{2{ '{4'd3, 4'd2, 4'd1, 4'd0 } }};
|
||||
if (array_simp !== 32'h3210_3210) $stop;
|
||||
|
||||
array_simp = '{2{ '{4{ 4'd3 }} }};
|
||||
if (array_simp !== 32'h3333_3333) $stop;
|
||||
|
||||
// Not legal in other simulators - replication doesn't match
|
||||
// However IEEE suggests this is legal.
|
||||
//array_simp = '{2{ '{2{ 4'd3, 4'd2 }} }}; // Note it's not '{3,2}
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
@@ -86,8 +101,8 @@ module t (/*AUTOARG*/
|
||||
else if (cnt[30:2]== 2) array_bg <= '{default:13};
|
||||
else if (cnt[30:2]== 3) array_bg <= '{0:4, 1:5, 2:6, 3:7};
|
||||
else if (cnt[30:2]== 4) array_bg <= '{2:15, default:13};
|
||||
else if (cnt[30:2]== 5) array_bg <= '{WA { {WB {2'b10}} }};
|
||||
else if (cnt[30:2]== 6) array_bg <= '{cnt+0, cnt+1, cnt+2, cnt+3};
|
||||
else if (cnt[30:2]== 5) array_bg <= '{WA { {WB/2 {2'b10}} }};
|
||||
else if (cnt[30:2]== 6) array_bg <= '{cnt[3:0]+0, cnt[3:0]+1, cnt[3:0]+2, cnt[3:0]+3};
|
||||
end else if (cnt[1:0]==2'd2) begin
|
||||
// chack array agains expected value
|
||||
if (cnt[30:2]== 0) begin if (array_bg !== 16'b0000000000000000) begin $display("%b", array_bg); $stop(); end end
|
||||
@@ -122,7 +137,7 @@ module t (/*AUTOARG*/
|
||||
else if (cnt[30:2]== 3) array_lt <= '{3:4, 2:5, 1:6, 0:7};
|
||||
else if (cnt[30:2]== 4) array_lt <= '{1:15, default:13};
|
||||
else if (cnt[30:2]== 5) array_lt <= '{WA { {WB/2 {2'b10}} }};
|
||||
else if (cnt[30:2]==10) array_lt <= '{cnt+0, cnt+1, cnt+2, cnt+3};
|
||||
else if (cnt[30:2]==10) array_lt <= '{cnt[3:0]+0, cnt[3:0]+1, cnt[3:0]+2, cnt[3:0]+3};
|
||||
end else if (cnt[1:0]==2'd2) begin
|
||||
// chack array agains expected value
|
||||
if (cnt[30:2]== 0) begin if (array_lt !== 16'b0000000000000000) begin $display("%b", array_lt); $stop(); end end
|
||||
|
||||
@@ -7,8 +7,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug355");
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
|
||||
@@ -25,7 +25,17 @@ module t (/*AUTOARG*/);
|
||||
array_simp[0][3],array_simp[0][2],array_simp[0][1],array_simp[0][0]} !== 32'h3210_1234) $stop;
|
||||
|
||||
// Doesn't seem to work for unpacked arrays in other simulators
|
||||
//array_simp <= '{2{ '{4{ 4'd3, 4'd2, 4'd1, 4'd0 }} }};
|
||||
array_simp = '{2{ '{4'd3, 4'd2, 4'd1, 4'd0 } }};
|
||||
if ({array_simp[1][3],array_simp[1][2],array_simp[1][1],array_simp[1][0],
|
||||
array_simp[0][3],array_simp[0][2],array_simp[0][1],array_simp[0][0]} !== 32'h3210_3210) $stop;
|
||||
|
||||
array_simp = '{2{ '{4{ 4'd3 }} }};
|
||||
if ({array_simp[1][3],array_simp[1][2],array_simp[1][1],array_simp[1][0],
|
||||
array_simp[0][3],array_simp[0][2],array_simp[0][1],array_simp[0][0]} !== 32'h3333_3333) $stop;
|
||||
|
||||
// Not legal in other simulators - replication doesn't match
|
||||
// However IEEE suggests this is legal.
|
||||
//array_simp = '{2{ '{2{ 4'd3, 4'd2 }} }}; // Note it's not '{3,2}
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
|
||||
@@ -27,12 +27,13 @@ module t (/*AUTOARG*/
|
||||
if (cyc!=0) begin
|
||||
cyc <= cyc + 1;
|
||||
toggle <= !cyc[0];
|
||||
if (cyc==7) assert (cyc[0] == cyc[1]); // bug743
|
||||
if (cyc==9) begin
|
||||
`ifdef FAILING_ASSERTIONS
|
||||
assert (0) else $info;
|
||||
assert (0) else $info("Info message");
|
||||
assert (0) else $info("Info message, cyc=%d", cyc);
|
||||
InWarningBlock: assert (0) else $warning("Warning....");
|
||||
InWarningBlock: assert (0) else $warning("Warning.... 1.0=%f 2.0=%f", 1.0, 2.0);
|
||||
InErrorBlock: assert (0) else $error("Error....");
|
||||
assert (0) else $fatal(1,"Fatal....");
|
||||
`endif
|
||||
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 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.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user