mirror of
https://github.com/verilator/verilator.git
synced 2026-10-03 00:23:35 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
761b9c9290 | ||
|
|
27686d8c2f | ||
|
|
d2b007005d | ||
|
|
f1ef30a5bb | ||
|
|
f1d9437c55 | ||
|
|
bcba5075e8 | ||
|
|
d6e8b0263c | ||
|
|
9aba617bad | ||
|
|
c24f7b1391 | ||
|
|
d4e27b635f | ||
|
|
c996d2870c | ||
|
|
bbd59f8a22 | ||
|
|
d3d359e757 | ||
|
|
ae763ea93d | ||
|
|
236b9e9761 | ||
|
|
e42c9dfd84 | ||
|
|
bebf5b291b | ||
|
|
1e3dcd203d | ||
|
|
cc7da63dec | ||
|
|
1baa2a2558 | ||
|
|
e63ff77b15 | ||
|
|
6cf9468477 | ||
|
|
b277bc8750 | ||
|
|
ba9da64207 | ||
|
|
9cb9f9c80b | ||
|
|
7a65df7636 | ||
|
|
eab46d547d | ||
|
|
3dd552c4a2 | ||
|
|
23bb045a72 | ||
|
|
7c834ad118 | ||
|
|
8e2617ab8d | ||
|
|
24fcae4f49 | ||
|
|
ce18674d88 | ||
|
|
6b8d9b5c36 | ||
|
|
81bf95763c | ||
|
|
2d64077fd0 | ||
|
|
38faebcf20 | ||
|
|
48d177a9d0 | ||
|
|
3b3a7f7354 | ||
|
|
99cd2818a1 | ||
|
|
5765e099a6 | ||
|
|
175d59ecba | ||
|
|
3bd3d01968 | ||
|
|
85e2a6bb45 | ||
|
|
84efd239a5 | ||
|
|
6a69813326 | ||
|
|
2c9dcc3913 | ||
|
|
20ea1ca018 | ||
|
|
0abde90933 | ||
|
|
e01b69198a | ||
|
|
53cd9d2403 | ||
|
|
3d0f5fc078 | ||
|
|
54eedcc739 | ||
|
|
ae6f5844da | ||
|
|
1bea845ceb | ||
|
|
d581582339 | ||
|
|
4eabc1992e | ||
|
|
316eb24bbb | ||
|
|
f0a8824efc | ||
|
|
345a5d5646 | ||
|
|
464679c78b | ||
|
|
28eeec1cf4 | ||
|
|
7d0dce3267 | ||
|
|
9e29625207 | ||
|
|
a767da4f3f | ||
|
|
7bd96c2876 | ||
|
|
70fd64dcd6 | ||
|
|
bb2822f4b5 | ||
|
|
ad21108b63 | ||
|
|
e6808a787c | ||
|
|
1fb2762725 | ||
|
|
cce55941d3 | ||
|
|
6c8d95e0e2 | ||
|
|
216a178b28 | ||
|
|
6594a54a95 | ||
|
|
a9a4cf061a | ||
|
|
b7f0e204cb | ||
|
|
e6a15f233b | ||
|
|
e0edb596ea | ||
|
|
f2fb77c15a | ||
|
|
772a3a97eb | ||
|
|
6c310836a1 | ||
|
|
75416a3016 | ||
|
|
ebef78a13e | ||
|
|
e71baca39b | ||
|
|
18eb210313 | ||
|
|
7d38a5e1f9 | ||
|
|
4386077e2d | ||
|
|
49dbfd2131 | ||
|
|
a80fce5ac1 | ||
|
|
891b981cab | ||
|
|
2dd87b8384 | ||
|
|
062eb85075 | ||
|
|
e1f6802f5f | ||
|
|
6cfcd50f12 |
@@ -3,6 +3,100 @@ 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.853 2013-09-30
|
||||
|
||||
**** Add --no-order-clock-delay to work around bug613. [Charlie Brej]
|
||||
|
||||
|
||||
* Verilator 3.852 2013-09-29
|
||||
|
||||
*** Support named function and task arguments. [Chris Randall]
|
||||
|
||||
*** Report SELRANGE warning for non-generate if, bug675. [Roland Kruse]
|
||||
|
||||
**** Fix ordering of $fgetc, msg1229. [Frederic Requin]
|
||||
|
||||
**** Fix --output-split-cfunc to count internal functions. [Chris Randall]
|
||||
|
||||
**** Fix crash on 32-bit Ubuntu, bug670. [Mark Jackson Pulver]
|
||||
|
||||
|
||||
* Verilator 3.851 2013-08-15
|
||||
|
||||
*** Fix ordering of clock enables with delayed assigns, bug613. [Jeremy Bennett]
|
||||
|
||||
*** Fix vpi_iterate on memory words, bug655. [Rich Porter]
|
||||
|
||||
**** Fix final duplicate declarations when non-inlined, bug661. [Charlie Brej]
|
||||
|
||||
**** Fix interface ports with comma lists, msg1058. [Ed Lander]
|
||||
|
||||
**** Fix parameter real conversion from integer.
|
||||
|
||||
**** Fix clang warnings, bug668. [Yutetsu Takatsukasa]
|
||||
|
||||
|
||||
* Verilator 3.850 2013-06-02
|
||||
|
||||
** Support interfaces and modports, bug102. [Byron Bradley, Jeremy Bennett]
|
||||
|
||||
*** Duplicate clock gate optimization on by default, bug621.
|
||||
|
||||
**** Fix arrayed input compile error, bug645. [Krzysztof Jankowski]
|
||||
|
||||
**** Fix GCC version runtime changes, bug651. [Jeremy Bennett]
|
||||
|
||||
**** Fix packed array select internal error, bug652. [Krzysztof Jankowski]
|
||||
|
||||
|
||||
* 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]
|
||||
|
||||
|
||||
* Verilator 3.846 2013-03-09
|
||||
|
||||
** IEEE 1800-2012 is now the default language. This adds 4 new keywords
|
||||
and updates the svdpi.h and vpi_user.h header files.
|
||||
|
||||
*** Add --report-unoptflat, bug611. [Jeremy Bennett]
|
||||
|
||||
*** Add duplicate clock gate optimization, msg980. [Varun Koyyalagunta]
|
||||
Disabled unless -OD or -O3 used, please try it as may get some
|
||||
significant speedups.
|
||||
|
||||
*** Fix wrong dot resolution under inlining. [Art Stamness]
|
||||
|
||||
**** Support pattern assignment features, bug616, bug617, bug618. [Ed Lander]
|
||||
|
||||
**** Support bind in $unit, bug602. [Ed Lander]
|
||||
|
||||
**** Support <number>'() sized casts, bug628. [Ed Lander]
|
||||
|
||||
**** Fix DETECTARRAY on packed structures, bug610. [Jeremy Bennett]
|
||||
|
||||
**** Fix LITENDIAN on unpacked structures, bug614. [Wai Sum Mong]
|
||||
|
||||
**** Fix 32-bit OS VPI scan issue, bug615. [Jeremy Bennett, Rich Porter]
|
||||
|
||||
**** Fix opening a VerilatedVcdC file multiple times, msg1021. [Frederic Requin]
|
||||
|
||||
**** Fix UNOPTFLAT circular array bounds crossing, bug630. [Jie Xu]
|
||||
|
||||
|
||||
* Verilator 3.845 2013/02/04
|
||||
|
||||
*** Fix nested packed arrays and struct, bug600. [Jeremy Bennett]
|
||||
|
||||
+1
-1
@@ -1,6 +1,5 @@
|
||||
^CVS/
|
||||
/CVS/
|
||||
\.gdbinit$
|
||||
\.git/
|
||||
\.svn/
|
||||
\.(bak|old)/
|
||||
@@ -23,6 +22,7 @@
|
||||
src/Makefile$
|
||||
src/Makefile_obj$
|
||||
include/verilated.mk$
|
||||
test_regress/.gdbinit$
|
||||
config.cache$
|
||||
config.status$
|
||||
verilator.log
|
||||
|
||||
@@ -121,6 +121,7 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
||||
include/vltstd/*.[chv]* \
|
||||
.*attributes */.*attributes */*/.*attributes \
|
||||
src/.*ignore src/*.in src/*.cpp src/*.[chly] src/astgen src/bisonpre src/*fix \
|
||||
src/.gdbinit \
|
||||
src/*.pl src/*.pod \
|
||||
test_*/.*ignore test_*/Makefile* test_*/*.cpp \
|
||||
test_*/*.pl test_*/*.v test_*/*.vc test_*/*.vh \
|
||||
|
||||
+101
-24
@@ -245,6 +245,7 @@ descriptions in the next sections for more information.
|
||||
+1364-2005ext+<ext> Use Verilog 2005 with file extension <ext>
|
||||
+1800-2005ext+<ext> Use SystemVerilog 2005 with file extension <ext>
|
||||
+1800-2009ext+<ext> Use SystemVerilog 2009 with file extension <ext>
|
||||
+1800-2012ext+<ext> Use SystemVerilog 2012 with file extension <ext>
|
||||
--assert Enable all assertions
|
||||
--autoflush Flush streams after all $displays
|
||||
--bbox-sys Blackbox unknown $system calls
|
||||
@@ -298,9 +299,13 @@ descriptions in the next sections for more information.
|
||||
-O3 High performance optimizations
|
||||
-O<optimization-letter> Selectable optimizations
|
||||
-o <executable> Name of final executable
|
||||
--no-order-clock-delay Disable ordering clock enable assignments
|
||||
--output-split <bytes> Split .cpp files into pieces
|
||||
--output-split-cfuncs <statements> Split .ccp functions
|
||||
--output-split-cfuncs <statements> Split .cpp functions
|
||||
--output-split-ctrace <statements> Split tracing functions
|
||||
--pins-bv <bits> Specify types for top level ports
|
||||
--pins-sc-uint Specify types for top level ports
|
||||
--pins-sc-biguint Specify types for top level ports
|
||||
--pins-uint8 Specify types for top level ports
|
||||
--pipe-filter <command> Filter all input through a script
|
||||
--prefix <topname> Name of top level class
|
||||
@@ -308,12 +313,13 @@ descriptions in the next sections for more information.
|
||||
--private Debugging; see docs
|
||||
--psl Enable PSL parsing
|
||||
--public Debugging; see docs
|
||||
--report-unoptflat Extra diagnostics for UNOPTFLAT
|
||||
--savable Enable model save-restore
|
||||
--sc Create SystemC output
|
||||
--sp Create SystemPerl output
|
||||
--stats Create statistics file
|
||||
-sv Enable SystemVerilog parsing
|
||||
+systemverilogext+<ext> Synonym for +1800-2009ext+<ext>
|
||||
+systemverilogext+<ext> Synonym for +1800-2012ext+<ext>
|
||||
--top-module <topname> Name of top level input module
|
||||
--trace Enable waveform creation
|
||||
--trace-depth <levels> Depth of tracing
|
||||
@@ -373,6 +379,8 @@ with the --exe option.
|
||||
|
||||
=item +1800-2009ext+I<ext>
|
||||
|
||||
=item +1800-2012ext+I<ext>
|
||||
|
||||
Specifies the language standard to be used with a specific filename
|
||||
extension, I<ext>.
|
||||
|
||||
@@ -584,7 +592,7 @@ C<--debug> is equivalent to C<--debugi 4>).
|
||||
|
||||
Select the language to be used by default when first processing each
|
||||
Verilog file. The language value must be "1364-1995", "1364-2001",
|
||||
"1364-2005", "1800-2005" or "1800-2009".
|
||||
"1364-2005", "1800-2005", "1800-2009" or "1800-2012".
|
||||
|
||||
Any language associated with a particular file extension (see the various
|
||||
+I<lang>ext+ options) will be used in preference to the language specified
|
||||
@@ -597,7 +605,7 @@ legacy mixed language designs, the various +I<lang>ext+ options should be
|
||||
used.
|
||||
|
||||
If no language is specified, either by this flag or +I<lang>ext+ options,
|
||||
then the latest SystemVerilog language (IEEE 1800-2009) is used.
|
||||
then the latest SystemVerilog language (IEEE 1800-2012) is used.
|
||||
|
||||
=item +define+I<var>+I<value>
|
||||
|
||||
@@ -782,6 +790,12 @@ mappings of optimizations to -O letters.
|
||||
Specify the name for the final executable built if using --exe. Defaults
|
||||
to the --prefix if not specified.
|
||||
|
||||
=item --no-order-clock-delay
|
||||
|
||||
Rarely needed. Disables a bug fix for ordering of clock enables with
|
||||
delayed assignments. This flag should only be used when suggested by the
|
||||
developers.
|
||||
|
||||
=item --output-split I<bytes>
|
||||
|
||||
Enables splitting the output .cpp/.sp files into multiple outputs. When a
|
||||
@@ -804,6 +818,11 @@ worse with decreasing split values. Note that this option is stronger than
|
||||
--output-split in the sense that --output-split will not split inside a
|
||||
function.
|
||||
|
||||
=item --output-split-ctrace I<statements>
|
||||
|
||||
Enables splitting trace functions in the output .cpp/.sp files into
|
||||
multiple functions. Defaults to same setting as --output-split-cfuncs.
|
||||
|
||||
=item --pins64
|
||||
|
||||
Backward compatible alias for "--pins-bv 65". Note that's a 65, not a 64.
|
||||
@@ -816,6 +835,20 @@ wide should use sc_bv's instead of uint32/vluint64_t's. The default is
|
||||
33". The more sc_bv is used, the worse for performance. Use the
|
||||
"/*verilator sc_bv*/" attribute to select specific ports to be sc_bv.
|
||||
|
||||
=item --pins-sc-uint
|
||||
|
||||
Specifies SystemC inputs/outputs of greater than 2 bits wide should use
|
||||
sc_uint between 2 and 64. When combined with the "--pins-sc-biguint"
|
||||
combination, it results in sc_uint being used between 2 and 64 and
|
||||
sc_biguint being used between 65 and 512.
|
||||
|
||||
=item --pins-sc-biguint
|
||||
|
||||
Specifies SystemC inputs/outputs of greater than 65 bits wide should use
|
||||
sc_biguint between 65 and 512, and sc_bv from 513 upwards. When combined
|
||||
with the "--pins-sc-uint" combination, it results in sc_uint being used
|
||||
between 2 and 64 and sc_biguint being used between 65 and 512.
|
||||
|
||||
=item --pins-uint8
|
||||
|
||||
Specifies SystemC inputs/outputs that are smaller than the --pins-bv
|
||||
@@ -877,6 +910,27 @@ inlining. This will also turn off inlining as if all modules had a
|
||||
/*verilator public_module*/, unless the module specifically enabled it with
|
||||
/*verilator inline_module*/.
|
||||
|
||||
=item --report-unoptflat
|
||||
|
||||
Extra diagnostics for UNOPTFLAT warnings. This includes for each loop, the
|
||||
10 widest variables in the loop, and the 10 most fanned out variables in
|
||||
the loop. These are candidates for splitting into multiple variables to
|
||||
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.
|
||||
|
||||
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
|
||||
printing. For example:
|
||||
|
||||
dot -Tpdf -O Vt_unoptflat_simple_2_35_unoptflat.dot
|
||||
|
||||
will generate a PDF Vt_unoptflat_simple_2_35_unoptflat.dot.pdf from the DOT
|
||||
file.
|
||||
|
||||
=item --savable
|
||||
|
||||
Enable including save and restore functions in the generated model.
|
||||
@@ -918,7 +972,7 @@ compatibility with other simulators.
|
||||
|
||||
=item +systemverilogext+I<ext>
|
||||
|
||||
A synonym for C<+1800-2009ext+>I<ext>.
|
||||
A synonym for C<+1800-2012ext+>I<ext>.
|
||||
|
||||
=item --top-module I<topname>
|
||||
|
||||
@@ -1027,10 +1081,10 @@ Disable the specified warning message.
|
||||
=item -Wno-lint
|
||||
|
||||
Disable all lint related warning messages, and all style warnings. This is
|
||||
equivalent to "-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.
|
||||
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.
|
||||
|
||||
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
|
||||
@@ -1059,9 +1113,10 @@ Enables the specified warning message.
|
||||
|
||||
Enable all lint related warning messages (note by default they are already
|
||||
enabled), but do not affect style messages. This is equivalent to
|
||||
"-Wwarn-CASEINCOMPLETE -Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASEWITHX
|
||||
-Wwarn-CMPCONST -Wwarn-ENDLABEL -Wwarn-IMPLICIT -Wwarn-LITENDIAN
|
||||
-Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED -Wwarn-WIDTH".
|
||||
"-Wwarn-ALWCOMBORDER -Wwarn-CASEINCOMPLETE -Wwarn-CASEOVERLAP -Wwarn-CASEX
|
||||
-Wwarn-CASEWITHX -Wwarn-CMPCONST -Wwarn-ENDLABEL -Wwarn-IMPLICIT
|
||||
-Wwarn-LITENDIAN -Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED
|
||||
-Wwarn-WIDTH".
|
||||
|
||||
=item -Wwarn-style
|
||||
|
||||
@@ -1878,29 +1933,25 @@ uwire keyword.
|
||||
|
||||
=head2 SystemVerilog 2005 (IEEE 1800-2005) Support
|
||||
|
||||
Verilator currently has some support for SystemVerilog synthesis
|
||||
constructs. As SystemVerilog features enter common usage they are added;
|
||||
please file a bug if a feature you need is missing.
|
||||
|
||||
Verilator supports ==? and !=? operators, ++ and -- in some contexts,
|
||||
$bits, $countones, $error, $fatal, $info, $isunknown, $onehot, $onehot0,
|
||||
$unit, $warning, always_comb, always_ff, always_latch, bit, byte, chandle,
|
||||
const, do-while, enum, export, final, import, int, logic, longint, package,
|
||||
program, shortint, struct, time, typedef, union, var, void, priority
|
||||
case/if, and unique case/if.
|
||||
const, do-while, enum, export, final, import, int, interface, logic,
|
||||
longint, modport, package, program, shortint, struct, time, typedef, union,
|
||||
var, void, priority case/if, and unique case/if.
|
||||
|
||||
It also supports .name and .* interconnection.
|
||||
|
||||
Verilator partially supports concurrent assert and cover statements; see
|
||||
the enclosed coverage tests for the syntax which is allowed.
|
||||
|
||||
=head2 SystemVerilog 2009 (IEEE 1800-2009) Support
|
||||
=head2 SystemVerilog 2012 (IEEE 1800-2012) Support
|
||||
|
||||
Verilator implements a full SystemVerilog 2009 preprocessor, including
|
||||
Verilator implements a full SystemVerilog 2012 preprocessor, including
|
||||
function call-like preprocessor defines, default define arguments,
|
||||
`__FILE__, `__LINE__ and `undefineall.
|
||||
|
||||
Verilator currently has some support for SystemVerilog 2009 synthesis
|
||||
Verilator currently has some support for SystemVerilog synthesis
|
||||
constructs. As SystemVerilog features enter common usage they are added;
|
||||
please file a bug if a feature you need is missing.
|
||||
|
||||
@@ -2563,6 +2614,12 @@ and continue keywords.
|
||||
Inside expressions may not include unpacked array traversal or $ as an
|
||||
upper bound. Case inside and case matches are also unsupported.
|
||||
|
||||
=item interface
|
||||
|
||||
Interfaces and modports, including with generated data types are supported.
|
||||
Generate blocks around modports are not supported, nor are virtual
|
||||
interfaces nor unnamed interfaces.
|
||||
|
||||
=item priority if, unique if
|
||||
|
||||
Priority and unique if's are treated as normal ifs and not asserted to be
|
||||
@@ -2673,6 +2730,20 @@ List of all warnings:
|
||||
|
||||
=over 4
|
||||
|
||||
=item ALWCOMBORDER
|
||||
|
||||
Warns that an always_comb block has a variable which is set after it is
|
||||
used. This may cause simulation-synthesis mismatches, as not all
|
||||
commercial simulators allow this ordering.
|
||||
|
||||
always_comb begin
|
||||
a = b;
|
||||
b = 1;
|
||||
end
|
||||
|
||||
Ignoring this warning will only suppress the lint check, it will simulate
|
||||
correctly.
|
||||
|
||||
=item ASSIGNIN
|
||||
|
||||
Error that an assignment is being made to an input signal. This is almost
|
||||
@@ -2840,7 +2911,8 @@ correctly.
|
||||
=item DETECTARRAY
|
||||
|
||||
Error when Verilator tries to deal with a combinatorial loop that could not be
|
||||
flattened, and which involves a structure. This typically ocurrs when
|
||||
flattened, and which involves a datatype which Verilator cannot handle, such
|
||||
as an unpacked struct or a large unpacked array. This typically ocurrs when
|
||||
-Wno-UNOPTFLAT has been used to override an UNOPTFLAT warning (see below).
|
||||
|
||||
The solution is to break the loop, as described for UNOPTFLAT.
|
||||
@@ -2926,7 +2998,7 @@ simulators.
|
||||
|
||||
=item LITENDIAN
|
||||
|
||||
Warns that a vector is declared with little endian bit numbering
|
||||
Warns that a packed vector is declared with little endian bit numbering
|
||||
(i.e. [0:7]). Big endian bit numbering is now the overwhelming standard,
|
||||
and little numbering is now thus often due to simple oversight instead of
|
||||
intent.
|
||||
@@ -3145,6 +3217,11 @@ The UNOPTFLAT warning may also occur where outputs from a block of logic
|
||||
are independent, but occur in the same always block. To fix this, use the
|
||||
isolate_assignments meta comment described above.
|
||||
|
||||
To assist in resolving UNOPTFLAT, the option C<--report-unoptflat> can be
|
||||
used, which will provide suggestions for variables that can be split up,
|
||||
and a graph of all the nodes connected in the loop. See the L<Arguments>
|
||||
section for more details.
|
||||
|
||||
Ignoring this warning will only slow simulations, it will simulate
|
||||
correctly.
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[3.845 2013-02-04])
|
||||
AC_INIT([Verilator],[3.853 2013-09-30])
|
||||
AC_CONFIG_HEADER(src/config_build.h)
|
||||
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h)
|
||||
|
||||
|
||||
+17
-5
@@ -769,6 +769,14 @@ void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) {
|
||||
_VL_STRING_TO_VINT(obits, destp, (int)output.length(), output.c_str());
|
||||
}
|
||||
|
||||
void VL_SFORMAT_X(int obits_ignored, string &output, const char* formatp, ...) {
|
||||
output = "";
|
||||
va_list ap;
|
||||
va_start(ap,formatp);
|
||||
_vl_vsformat(output, formatp, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
string VL_SFORMATF_NX(const char* formatp, ...) {
|
||||
VL_STATIC_OR_THREAD string output; // static only for speed
|
||||
output = "";
|
||||
@@ -1075,6 +1083,10 @@ const char* Verilated::commandArgsPlusMatch(const char* prefixp) {
|
||||
return VerilatedImp::argPlusMatch(prefixp).c_str();
|
||||
}
|
||||
|
||||
void Verilated::internalsDump() {
|
||||
VerilatedImp::internalsDump();
|
||||
}
|
||||
|
||||
void Verilated::scopesDump() {
|
||||
VerilatedImp::scopesDump();
|
||||
}
|
||||
@@ -1110,7 +1122,7 @@ vluint32_t VerilatedVar::entSize() const {
|
||||
case VLVT_UINT16: size=sizeof(SData); break;
|
||||
case VLVT_UINT32: size=sizeof(IData); break;
|
||||
case VLVT_UINT64: size=sizeof(QData); break;
|
||||
case VLVT_WDATA: size=VL_WORDS_I(range().bits())*sizeof(IData); break;
|
||||
case VLVT_WDATA: size=VL_WORDS_I(range().elements())*sizeof(IData); break;
|
||||
default: size=0; break;
|
||||
}
|
||||
return size;
|
||||
@@ -1182,11 +1194,11 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap,
|
||||
int msb = va_arg(ap,int);
|
||||
int lsb = va_arg(ap,int);
|
||||
if (i==0) {
|
||||
var.m_range.m_lhs = msb;
|
||||
var.m_range.m_rhs = lsb;
|
||||
var.m_range.m_left = msb;
|
||||
var.m_range.m_right = lsb;
|
||||
} else if (i==1) {
|
||||
var.m_array.m_lhs = msb;
|
||||
var.m_array.m_rhs = lsb;
|
||||
var.m_array.m_left = msb;
|
||||
var.m_array.m_right = lsb;
|
||||
} else {
|
||||
// We could have a linked list of ranges, but really this whole thing needs
|
||||
// to be generalized to support structs and unions, etc.
|
||||
|
||||
+37
-4
@@ -145,7 +145,7 @@ public:
|
||||
# define VL_CELL(instname,type) ///< Declare a cell, ala SP_CELL
|
||||
|
||||
/// Declare a module, ala SC_MODULE
|
||||
# define VL_MODULE(modname) struct modname : public VerilatedModule
|
||||
# define VL_MODULE(modname) class modname : public VerilatedModule
|
||||
|
||||
/// Constructor, ala SC_CTOR
|
||||
# define VL_CTOR(modname) modname(const char* __VCname="")
|
||||
@@ -216,9 +216,8 @@ public: // But internals only - called from VerilatedModule's
|
||||
//===========================================================================
|
||||
/// Verilator global static information class
|
||||
|
||||
struct Verilated {
|
||||
class Verilated {
|
||||
// MEMBERS
|
||||
private:
|
||||
// Slow path variables
|
||||
static VerilatedVoidCb s_flushCb; ///< Flush callback function
|
||||
|
||||
@@ -294,6 +293,11 @@ public:
|
||||
static const char* productName() { return VERILATOR_PRODUCT; }
|
||||
static const char* productVersion() { return VERILATOR_VERSION; }
|
||||
|
||||
/// For debugging, print much of the Verilator internal state.
|
||||
/// The output of this function may change in future
|
||||
/// releases - contact the authors before production use.
|
||||
static void internalsDump();
|
||||
|
||||
/// For debugging, print text list of all scope names with
|
||||
/// dpiImport/Export context. This function may change in future
|
||||
/// releases - contact the authors before production use.
|
||||
@@ -547,6 +551,7 @@ static inline void VL_ASSIGNBIT_WO(int, int bit, WDataOutP owp, IData) {
|
||||
//===================================================================
|
||||
// SYSTEMC OPERATORS
|
||||
// Copying verilog format to systemc integers and bit vectors.
|
||||
// Get a SystemC variable
|
||||
|
||||
#define VL_ASSIGN_ISI(obits,vvar,svar) { (vvar) = VL_CLEAN_II((obits),(obits),(svar).read()); }
|
||||
#define VL_ASSIGN_QSQ(obits,vvar,svar) { (vvar) = VL_CLEAN_QQ((obits),(obits),(svar).read()); }
|
||||
@@ -564,7 +569,21 @@ static inline void VL_ASSIGNBIT_WO(int, int bit, WDataOutP owp, IData) {
|
||||
owp[words-1] &= VL_MASK_I(obits); \
|
||||
}
|
||||
|
||||
#define VL_ASSIGN_ISU(obits,vvar,svar) { (vvar) = VL_CLEAN_II((obits),(obits),(svar).read().to_uint()); }
|
||||
#define VL_ASSIGN_QSU(obits,vvar,svar) { (vvar) = VL_CLEAN_QQ((obits),(obits),(svar).read().to_uint64()); }
|
||||
#define VL_ASSIGN_WSB(obits,owp,svar) { \
|
||||
int words = VL_WORDS_I(obits); \
|
||||
sc_biguint<obits> _butemp = (svar).read(); \
|
||||
for (int i=0; i < words; i++) { \
|
||||
int msb = ((i+1)*VL_WORDSIZE) - 1; \
|
||||
msb = (msb >= obits) ? (obits-1) : msb; \
|
||||
owp[i] = _butemp.range(msb,i*VL_WORDSIZE).to_uint(); \
|
||||
} \
|
||||
owp[words-1] &= VL_MASK_I(obits); \
|
||||
}
|
||||
|
||||
// Copying verilog format from systemc integers and bit vectors.
|
||||
// Set a SystemC variable
|
||||
|
||||
#define VL_ASSIGN_SII(obits,svar,vvar) { (svar).write(vvar); }
|
||||
#define VL_ASSIGN_SQQ(obits,svar,vvar) { (svar).write(vvar); }
|
||||
@@ -586,6 +605,20 @@ static inline void VL_ASSIGNBIT_WO(int, int bit, WDataOutP owp, IData) {
|
||||
svar.write(_bvtemp); \
|
||||
}
|
||||
|
||||
#define VL_ASSIGN_SUI(obits,svar,rd) { (svar).write(rd); }
|
||||
#define VL_ASSIGN_SUQ(obits,svar,rd) { (svar).write(rd); }
|
||||
#define VL_ASSIGN_SBI(obits,svar,rd) { (svar).write(rd); }
|
||||
#define VL_ASSIGN_SBQ(obits,svar,rd) { (svar).write(rd); }
|
||||
#define VL_ASSIGN_SBW(obits,svar,rwp) { \
|
||||
sc_biguint<obits> _butemp; \
|
||||
for (int i=0; i < VL_WORDS_I(obits); i++) { \
|
||||
int msb = ((i+1)*VL_WORDSIZE) - 1; \
|
||||
msb = (msb >= obits) ? (obits-1) : msb; \
|
||||
_butemp.range(msb,i*VL_WORDSIZE) = rwp[i]; \
|
||||
} \
|
||||
svar.write(_butemp); \
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Extending sizes
|
||||
|
||||
@@ -837,7 +870,7 @@ static inline WDataOutP VL_OR_W(int words, WDataOutP owp,WDataInP lwp,WDataInP r
|
||||
}
|
||||
// EMIT_RULE: VL_CHANGEXOR: oclean=1; obits=32; lbits==rbits;
|
||||
static inline IData VL_CHANGEXOR_W(int words, WDataInP lwp,WDataInP rwp){
|
||||
IData od;
|
||||
IData od = 0;
|
||||
for (int i=0; (i < words); i++) od |= (lwp[i] ^ rwp[i]);
|
||||
return(od);
|
||||
}
|
||||
|
||||
@@ -41,11 +41,15 @@ inline string VL_CVT_PACK_STR_NQ(QData lhs) {
|
||||
IData lw[2]; VL_SET_WQ(lw, lhs);
|
||||
return VL_CVT_PACK_STR_NW(2, lw);
|
||||
}
|
||||
inline string VL_CVT_PACK_STR_NQ(string lhs) {
|
||||
return lhs;
|
||||
}
|
||||
inline string VL_CVT_PACK_STR_NI(IData lhs) {
|
||||
IData lw[1]; lw[0] = lhs;
|
||||
return VL_CVT_PACK_STR_NW(1, lw);
|
||||
}
|
||||
|
||||
extern void VL_SFORMAT_X(int obits_ignored, string &output, const char* formatp, ...);
|
||||
extern string VL_SFORMATF_NX(const char* formatp, ...);
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+28
-2
@@ -78,6 +78,18 @@ public: // But only for verilated*.cpp
|
||||
m_fdps[2] = stderr;
|
||||
}
|
||||
~VerilatedImp() {}
|
||||
static void internalsDump() {
|
||||
VL_PRINTF("internalsDump:\n");
|
||||
VL_PRINTF(" Argv:");
|
||||
for (ArgVec::iterator it=s_s.m_argVec.begin(); it!=s_s.m_argVec.end(); ++it) {
|
||||
VL_PRINTF(" %s",it->c_str());
|
||||
}
|
||||
VL_PRINTF("\n");
|
||||
VL_PRINTF(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
|
||||
scopesDump();
|
||||
exportsDump();
|
||||
userDump();
|
||||
}
|
||||
|
||||
// METHODS - arguments
|
||||
static void commandArgs(int argc, const char** argv) {
|
||||
@@ -129,6 +141,14 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
static void userDump() {
|
||||
bool first = true;
|
||||
for (UserMap::iterator it=s_s.m_userMap.begin(); it!=s_s.m_userMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF(" userDump:\n"); first=false; }
|
||||
VL_PRINTF(" DPI_USER_DATA scope %p key %p: %p\n",
|
||||
it->first.first, it->first.second, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - scope name
|
||||
@@ -150,9 +170,8 @@ public: // But only for verilated*.cpp
|
||||
ScopeNameMap::iterator it=s_s.m_nameMap.find(scopep->name());
|
||||
if (it != s_s.m_nameMap.end()) s_s.m_nameMap.erase(it);
|
||||
}
|
||||
|
||||
static void scopesDump() {
|
||||
VL_PRINTF("scopesDump:\n");
|
||||
VL_PRINTF(" scopesDump:\n");
|
||||
for (ScopeNameMap::iterator it=s_s.m_nameMap.begin(); it!=s_s.m_nameMap.end(); ++it) {
|
||||
const VerilatedScope* scopep = it->second;
|
||||
scopep->scopeDump();
|
||||
@@ -194,6 +213,13 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
return "*UNKNOWN*";
|
||||
}
|
||||
static void exportsDump() {
|
||||
bool first = true;
|
||||
for (ExportNameMap::iterator it=s_s.m_exportMap.begin(); it!=s_s.m_exportMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF(" exportDump:\n"); first=false; }
|
||||
VL_PRINTF(" DPI_EXPORT_NAME %05d: %s\n", it->second, it->first);
|
||||
}
|
||||
}
|
||||
// We don't free up m_exportMap until the end, because we can't be sure
|
||||
// what other models are using the assigned funcnum's.
|
||||
|
||||
|
||||
@@ -45,19 +45,20 @@ struct VerilatedCStrCmp {
|
||||
//===========================================================================
|
||||
/// Verilator range
|
||||
|
||||
// See also V3Ast::VNumRange
|
||||
class VerilatedRange {
|
||||
int m_lhs;
|
||||
int m_rhs;
|
||||
int m_left;
|
||||
int m_right;
|
||||
protected:
|
||||
friend class VerilatedVar;
|
||||
friend class VerilatedScope;
|
||||
VerilatedRange() : m_lhs(0), m_rhs(0) {}
|
||||
void sets(int lhs, int rhs) { m_lhs=lhs; m_rhs=rhs; }
|
||||
VerilatedRange() : m_left(0), m_right(0) {}
|
||||
void sets(int left, int right) { m_left=left; m_right=right; }
|
||||
public:
|
||||
~VerilatedRange() {}
|
||||
int lhs() const { return m_lhs; }
|
||||
int rhs() const { return m_rhs; }
|
||||
int bits() const { return (VL_LIKELY(m_lhs>=m_rhs)?(m_lhs-m_rhs+1):(m_rhs-m_lhs+1)); }
|
||||
int left() const { return m_left; }
|
||||
int right() const { return m_right; }
|
||||
int elements() const { return (VL_LIKELY(m_left>=m_right)?(m_left-m_right+1):(m_right-m_left+1)); }
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
@@ -92,7 +93,8 @@ public:
|
||||
//======================================================================
|
||||
/// Types
|
||||
|
||||
struct VerilatedVarNameMap : public map<const char*, VerilatedVar, VerilatedCStrCmp> {
|
||||
class VerilatedVarNameMap : public map<const char*, VerilatedVar, VerilatedCStrCmp> {
|
||||
public:
|
||||
VerilatedVarNameMap() {}
|
||||
~VerilatedVarNameMap() {}
|
||||
};
|
||||
|
||||
@@ -157,6 +157,8 @@ void VerilatedVcd::openNext (bool incFilename) {
|
||||
|
||||
void VerilatedVcd::makeNameMap() {
|
||||
// Take signal information from each module and build m_namemapp
|
||||
deleteNameMap();
|
||||
m_nextCode = 1;
|
||||
m_namemapp = new NameMap;
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) {
|
||||
VerilatedVcdCallInfo *cip = m_callbacks[ent];
|
||||
@@ -183,15 +185,20 @@ void VerilatedVcd::makeNameMap() {
|
||||
newname += hiername;
|
||||
newmapp->insert(make_pair(newname,decl));
|
||||
}
|
||||
delete m_namemapp; m_namemapp=NULL;
|
||||
deleteNameMap();
|
||||
m_namemapp = newmapp;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::deleteNameMap() {
|
||||
if (m_namemapp) { delete m_namemapp; m_namemapp=NULL; }
|
||||
}
|
||||
|
||||
VerilatedVcd::~VerilatedVcd() {
|
||||
close();
|
||||
if (m_wrBufp) { delete[] m_wrBufp; m_wrBufp=NULL; }
|
||||
if (m_sigs_oldvalp) { delete[] m_sigs_oldvalp; m_sigs_oldvalp=NULL; }
|
||||
deleteNameMap();
|
||||
// Remove from list of traces
|
||||
vector<VerilatedVcd*>::iterator pos = find(s_vcdVecp.begin(), s_vcdVecp.end(), this);
|
||||
if (pos != s_vcdVecp.end()) { s_vcdVecp.erase(pos); }
|
||||
@@ -414,7 +421,7 @@ void VerilatedVcd::dumpHeader () {
|
||||
assert (m_modDepth==0);
|
||||
|
||||
// Reclaim storage
|
||||
delete m_namemapp;
|
||||
deleteNameMap();
|
||||
}
|
||||
|
||||
void VerilatedVcd::module (string name) {
|
||||
|
||||
@@ -57,7 +57,8 @@ typedef void (*VerilatedVcdCallback_t)(VerilatedVcd* vcdp, void* userthis, vluin
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcd
|
||||
/// Create a SystemPerl VCD dump
|
||||
/// Base class to create a Verilator VCD dump
|
||||
/// This is an internally used class - see VerilatedVcdC for what to call from applications
|
||||
|
||||
class VerilatedVcd {
|
||||
private:
|
||||
@@ -100,6 +101,7 @@ private:
|
||||
void closeErr();
|
||||
void openNext();
|
||||
void makeNameMap();
|
||||
void deleteNameMap();
|
||||
void printIndent (int levelchange);
|
||||
void printStr (const char* str);
|
||||
void printQuad (vluint64_t n);
|
||||
@@ -400,8 +402,12 @@ public:
|
||||
bool isOpen() const { return m_sptrace.isOpen(); }
|
||||
// METHODS
|
||||
/// Open a new VCD file
|
||||
/// This includes a complete header dump each time it is called,
|
||||
/// just as if this object was deleted and reconstructed.
|
||||
void open (const char* filename) { m_sptrace.open(filename); }
|
||||
/// Continue a VCD dump by rotating to a new file name
|
||||
/// The header is only in the first file created, this allows
|
||||
/// "cat" to be used to combine the header plus any number of data files.
|
||||
void openNext (bool incFilename=true) { m_sptrace.openNext(incFilename); }
|
||||
/// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(size_t rolloverMB) { m_sptrace.rolloverMB(rolloverMB); };
|
||||
|
||||
+126
-56
@@ -94,6 +94,9 @@ public:
|
||||
virtual const char* name() { return "<null>"; }
|
||||
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 vpiHandle dovpi_scan() { return 0; }
|
||||
};
|
||||
|
||||
@@ -112,6 +115,7 @@ public:
|
||||
}
|
||||
virtual ~VerilatedVpioCb() {}
|
||||
static inline VerilatedVpioCb* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioCb*>((VerilatedVpio*)h); }
|
||||
virtual const vluint32_t type() { return vpiCallback; }
|
||||
vluint32_t reason() const { return m_cbData.reason; }
|
||||
VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
|
||||
t_cb_data* cb_datap() { return &(m_cbData); }
|
||||
@@ -124,19 +128,20 @@ public:
|
||||
VerilatedVpioConst(vlsint32_t num) : m_num(num) {}
|
||||
virtual ~VerilatedVpioConst() {}
|
||||
static inline VerilatedVpioConst* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioConst*>((VerilatedVpio*)h); }
|
||||
virtual const vluint32_t type() { return vpiUndefined; }
|
||||
vlsint32_t num() const { return m_num; }
|
||||
};
|
||||
|
||||
class VerilatedVpioRange : public VerilatedVpio {
|
||||
vlsint32_t m_lhs; // Ranges can be signed
|
||||
vlsint32_t m_rhs;
|
||||
const VerilatedRange* m_range;
|
||||
bool m_iteration;
|
||||
public:
|
||||
VerilatedVpioRange(vlsint32_t lhs, vlsint32_t rhs) : m_lhs(lhs), m_rhs(rhs), m_iteration(0) {}
|
||||
VerilatedVpioRange(const VerilatedRange* range) : m_range(range), m_iteration(0) {}
|
||||
virtual ~VerilatedVpioRange() {}
|
||||
static inline VerilatedVpioRange* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioRange*>((VerilatedVpio*)h); }
|
||||
vlsint32_t lhs() const { return m_lhs; }
|
||||
vlsint32_t rhs() const { return m_rhs; }
|
||||
virtual const vluint32_t type() { return vpiRange; }
|
||||
virtual const vluint32_t size() const { return m_range->elements(); }
|
||||
virtual const VerilatedRange* rangep() const { return m_range; }
|
||||
int iteration() const { return m_iteration; }
|
||||
void iterationInc() { ++m_iteration; }
|
||||
virtual vpiHandle dovpi_scan() {
|
||||
@@ -157,6 +162,7 @@ public:
|
||||
: m_scopep(scopep) {}
|
||||
virtual ~VerilatedVpioScope() {}
|
||||
static inline VerilatedVpioScope* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioScope*>((VerilatedVpio*)h); }
|
||||
virtual const vluint32_t type() { return vpiScope; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
virtual const char* name() { return m_scopep->name(); }
|
||||
virtual const char* fullname() { return m_scopep->name(); }
|
||||
@@ -178,7 +184,7 @@ public:
|
||||
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: m_varp(varp), m_scopep(scopep), m_index(0) {
|
||||
m_prevDatap = NULL;
|
||||
m_mask.u32 = VL_MASK_I(varp->range().bits());
|
||||
m_mask.u32 = VL_MASK_I(varp->range().elements());
|
||||
m_entSize = varp->entSize();
|
||||
m_varDatap = varp->datap();
|
||||
}
|
||||
@@ -191,6 +197,11 @@ public:
|
||||
vluint32_t mask() const { return m_mask.u32; }
|
||||
vluint8_t mask_byte(int idx) { return m_mask.u8[idx & 3]; }
|
||||
vluint32_t entSize() const { return m_entSize; }
|
||||
const vluint32_t index() { return m_index; }
|
||||
virtual const vluint32_t type() { return (varp()->dims()>1) ? vpiMemory : vpiReg; /* but might be wire, logic */ }
|
||||
virtual const vluint32_t size() { return range().elements(); }
|
||||
const VerilatedRange& range() { return m_varp->dims()?m_varp->array():m_varp->range(); }
|
||||
virtual const VerilatedRange* rangep() { return &range(); }
|
||||
virtual const char* name() { return m_varp->name(); }
|
||||
virtual const char* fullname() {
|
||||
VL_STATIC_OR_THREAD string out;
|
||||
@@ -207,16 +218,19 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioVarIndex : public VerilatedVpioVar {
|
||||
class VerilatedVpioMemoryWord : public VerilatedVpioVar {
|
||||
public:
|
||||
VerilatedVpioVarIndex(const VerilatedVar* varp, const VerilatedScope* scopep,
|
||||
VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep,
|
||||
vlsint32_t index, int offset)
|
||||
: VerilatedVpioVar(varp, scopep) {
|
||||
m_index = index;
|
||||
m_varDatap = ((vluint8_t*)varp->datap()) + entSize()*offset;
|
||||
}
|
||||
virtual ~VerilatedVpioVarIndex() {}
|
||||
static inline VerilatedVpioVarIndex* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioVarIndex*>((VerilatedVpio*)h); }
|
||||
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 char* fullname() {
|
||||
VL_STATIC_OR_THREAD string out;
|
||||
char num[20]; sprintf(num,"%d",m_index);
|
||||
@@ -234,6 +248,7 @@ public:
|
||||
: m_scopep(scopep), m_started(false) { }
|
||||
virtual ~VerilatedVpioVarIter() {}
|
||||
static inline VerilatedVpioVarIter* castp(vpiHandle h) { return dynamic_cast<VerilatedVpioVarIter*>((VerilatedVpio*)h); }
|
||||
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; }
|
||||
@@ -248,6 +263,28 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioMemoryWordIter : public VerilatedVpio {
|
||||
const vpiHandle m_handle;
|
||||
const VerilatedVar* m_varp;
|
||||
vlsint32_t m_iteration;
|
||||
vlsint32_t m_direction;
|
||||
bool m_done;
|
||||
public:
|
||||
VerilatedVpioMemoryWordIter(const vpiHandle handle, const VerilatedVar* varp)
|
||||
: m_handle(handle), m_varp(varp), m_iteration(varp->array().right()), m_direction(VL_LIKELY(varp->array().left()>varp->array().right())?1:-1), m_done(false) { }
|
||||
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; }
|
||||
virtual vpiHandle dovpi_scan() {
|
||||
vpiHandle result;
|
||||
if (m_done) return 0;
|
||||
result = vpi_handle_by_index(m_handle, m_iteration);
|
||||
iterationInc();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
struct VerilatedVpiTimedCbsCmp {
|
||||
@@ -260,13 +297,18 @@ struct VerilatedVpiTimedCbsCmp {
|
||||
}
|
||||
};
|
||||
|
||||
struct VerilatedVpiError;
|
||||
class VerilatedVpiError;
|
||||
|
||||
class VerilatedVpi {
|
||||
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime+1 }; // Maxium callback reason
|
||||
typedef set<VerilatedVpioCb*> VpioCbSet;
|
||||
typedef set<pair<QData,VerilatedVpioCb*>,VerilatedVpiTimedCbsCmp > VpioTimedCbs;
|
||||
|
||||
struct product_info {
|
||||
PLI_BYTE8* product;
|
||||
PLI_BYTE8* version;
|
||||
};
|
||||
|
||||
VpioCbSet m_cbObjSets[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason
|
||||
VpioTimedCbs m_timedCbs; // Time based callbacks
|
||||
VerilatedVpiError* m_errorInfop; // Container for vpi error info
|
||||
@@ -545,15 +587,15 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
if (VL_LIKELY(varop)) {
|
||||
if (varop->varp()->dims()<2) return 0;
|
||||
if (VL_LIKELY(varop->varp()->array().lhs() >= varop->varp()->array().rhs())) {
|
||||
if (VL_UNLIKELY(indx > varop->varp()->array().lhs() || indx < varop->varp()->array().rhs())) return 0;
|
||||
return (new VerilatedVpioVarIndex(varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->array().rhs()))
|
||||
if (VL_LIKELY(varop->varp()->array().left() >= varop->varp()->array().right())) {
|
||||
if (VL_UNLIKELY(indx > varop->varp()->array().left() || indx < varop->varp()->array().right())) return 0;
|
||||
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->array().right()))
|
||||
->castVpiHandle();
|
||||
} else {
|
||||
if (VL_UNLIKELY(indx < varop->varp()->array().lhs() || indx > varop->varp()->array().rhs())) return 0;
|
||||
return (new VerilatedVpioVarIndex(varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->array().lhs()))
|
||||
if (VL_UNLIKELY(indx < varop->varp()->array().left() || indx > varop->varp()->array().right())) return 0;
|
||||
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->array().left()))
|
||||
->castVpiHandle();
|
||||
}
|
||||
} else {
|
||||
@@ -568,21 +610,32 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_PRINTF("-vltVpi: vpi_handle %d %p\n",type,object););
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
switch (type) {
|
||||
case vpiLeftRange: // FALLTHRU
|
||||
case vpiLeftRange: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
if (VL_UNLIKELY(!vop->rangep())) return 0;
|
||||
return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle();
|
||||
}
|
||||
case vpiRightRange: {
|
||||
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
|
||||
vluint32_t num = ((type==vpiLeftRange)
|
||||
? vop->varp()->range().lhs()
|
||||
: vop->varp()->range().rhs());
|
||||
return (new VerilatedVpioConst(num))->castVpiHandle();
|
||||
} else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) {
|
||||
vluint32_t num = ((type==vpiLeftRange)
|
||||
? vop->lhs()
|
||||
: vop->rhs());
|
||||
return (new VerilatedVpioConst(num))->castVpiHandle();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
if (VL_UNLIKELY(!vop->rangep())) return 0;
|
||||
return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle();
|
||||
}
|
||||
case vpiIndex: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
return (new VerilatedVpioConst(vop->index()))->castVpiHandle();
|
||||
}
|
||||
case vpiScope: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
return (new VerilatedVpioScope(vop->scopep()))->castVpiHandle();
|
||||
}
|
||||
case vpiParent: {
|
||||
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
return (new VerilatedVpioVar(vop->varp(), vop->scopep()))->castVpiHandle();
|
||||
}
|
||||
default:
|
||||
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
||||
@@ -600,13 +653,25 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
_VL_VPI_ERROR_RESET(); // reset vpi error status
|
||||
switch (type) {
|
||||
case vpiMemoryWord: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
if (vop->varp()->dims() < 2) return 0;
|
||||
if (vop->varp()->dims() > 2) {
|
||||
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: %s, object %s has unsupported number of indices (%d)",
|
||||
VL_FUNC, VerilatedVpiError::strFromVpiMethod(type), vop->fullname() , vop->varp()->dims());
|
||||
}
|
||||
return (new VerilatedVpioMemoryWordIter(object, vop->varp()))->castVpiHandle();
|
||||
}
|
||||
case vpiRange: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
if (vop->varp()->dims() < 2) return 0;
|
||||
// Unsupported is multidim list
|
||||
return ((new VerilatedVpioRange(vop->varp()->array().lhs(),
|
||||
vop->varp()->array().rhs()))
|
||||
->castVpiHandle());
|
||||
if (vop->varp()->dims() > 2) {
|
||||
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: %s, object %s has unsupported number of indices (%d)",
|
||||
VL_FUNC, VerilatedVpiError::strFromVpiMethod(type), vop->fullname() , vop->varp()->dims());
|
||||
}
|
||||
return ((new VerilatedVpioRange(vop->rangep()))->castVpiHandle());
|
||||
}
|
||||
case vpiReg: {
|
||||
VerilatedVpioScope* vop = VerilatedVpioScope::castp(object);
|
||||
@@ -639,9 +704,9 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
|
||||
return VL_TIME_PRECISION;
|
||||
}
|
||||
case vpiType: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
VerilatedVpio* vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
return ((vop->varp()->dims()>1) ? vpiMemory : vpiReg);
|
||||
return vop->type();
|
||||
}
|
||||
case vpiDirection: {
|
||||
// By forthought, the directions already are vpi enumerated
|
||||
@@ -649,11 +714,16 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
return vop->varp()->vldir();
|
||||
}
|
||||
case vpiScalar: // FALLTHRU
|
||||
case vpiVector: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
if (vop->varp()->dims()==0) return 0;
|
||||
else return 1;
|
||||
return (property==vpiVector)^(vop->varp()->dims()==0);
|
||||
}
|
||||
case vpiSize: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
return vop->size();
|
||||
}
|
||||
default:
|
||||
_VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
||||
@@ -730,7 +800,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
out[0].bval = 0;
|
||||
return;
|
||||
case VLVT_WDATA: {
|
||||
int words = VL_WORDS_I(vop->varp()->range().bits());
|
||||
int words = VL_WORDS_I(vop->varp()->range().elements());
|
||||
if (VL_UNLIKELY(words >= VL_MULS_MAX_WORDS)) {
|
||||
vl_fatal(__FILE__,__LINE__,"", "vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
|
||||
}
|
||||
@@ -763,7 +833,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
case VLVT_UINT32:
|
||||
case VLVT_UINT64:
|
||||
case VLVT_WDATA: {
|
||||
int bits = vop->varp()->range().bits();
|
||||
int bits = vop->varp()->range().elements();
|
||||
CData* datap = ((CData*)(vop->varDatap()));
|
||||
int i;
|
||||
if (bits > outStrSz) {
|
||||
@@ -792,8 +862,8 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
case VLVT_UINT32:
|
||||
case VLVT_UINT64:
|
||||
case VLVT_WDATA: {
|
||||
int chars = (vop->varp()->range().bits()+2)/3;
|
||||
int bytes = VL_BYTES_I(vop->varp()->range().bits());
|
||||
int chars = (vop->varp()->range().elements()+2)/3;
|
||||
int bytes = VL_BYTES_I(vop->varp()->range().elements());
|
||||
CData* datap = ((CData*)(vop->varDatap()));
|
||||
int i;
|
||||
if (chars > outStrSz) {
|
||||
@@ -815,7 +885,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
if (i==(chars-1)) {
|
||||
// most signifcant char, mask off non existant bits when vector
|
||||
// size is not a multiple of 3
|
||||
unsigned int rem = vop->varp()->range().bits() % 3;
|
||||
unsigned int rem = vop->varp()->range().elements() % 3;
|
||||
if (rem) {
|
||||
// generate bit mask & zero non existant bits
|
||||
val &= (1<<rem)-1;
|
||||
@@ -839,7 +909,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
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_UINT64: snprintf(outStr, outStrSz+1, "%lu", (unsigned long)*((QData*)(vop->varDatap()))); return;
|
||||
case VLVT_UINT64: snprintf(outStr, outStrSz+1, "%llu", (unsigned long long)*((QData*)(vop->varDatap()))); return;
|
||||
default:
|
||||
strcpy(outStr, "-1");
|
||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s, maximum limit is 64 bits",
|
||||
@@ -854,7 +924,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
case VLVT_UINT32:
|
||||
case VLVT_UINT64:
|
||||
case VLVT_WDATA: {
|
||||
int chars = (vop->varp()->range().bits()+3)>>2;
|
||||
int chars = (vop->varp()->range().elements()+3)>>2;
|
||||
CData* datap = ((CData*)(vop->varDatap()));
|
||||
int i;
|
||||
if (chars > outStrSz) {
|
||||
@@ -869,7 +939,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
if (i==(chars-1)) {
|
||||
// most signifcant char, mask off non existant bits when vector
|
||||
// size is not a multiple of 4
|
||||
unsigned int rem = vop->varp()->range().bits() & 3;
|
||||
unsigned int rem = vop->varp()->range().elements() & 3;
|
||||
if (rem) {
|
||||
// generate bit mask & zero non existant bits
|
||||
val &= (1<<rem)-1;
|
||||
@@ -893,7 +963,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value value_p) {
|
||||
case VLVT_UINT32:
|
||||
case VLVT_UINT64:
|
||||
case VLVT_WDATA: {
|
||||
int bytes = VL_BYTES_I(vop->varp()->range().bits());
|
||||
int bytes = VL_BYTES_I(vop->varp()->range().elements());
|
||||
CData* datap = ((CData*)(vop->varDatap()));
|
||||
int i;
|
||||
if (bytes > outStrSz) {
|
||||
@@ -984,7 +1054,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
||||
*((IData*)(vop->varDatap())) = value_p->value.vector[0].aval & vop->mask();
|
||||
return object;
|
||||
case VLVT_WDATA: {
|
||||
int words = VL_WORDS_I(vop->varp()->range().bits());
|
||||
int words = VL_WORDS_I(vop->varp()->range().elements());
|
||||
WDataOutP datap = ((IData*)(vop->varDatap()));
|
||||
for (int i=0; i<words; i++) {
|
||||
datap[i] = value_p->value.vector[i].aval;
|
||||
@@ -1013,7 +1083,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
||||
case VLVT_UINT32:
|
||||
case VLVT_UINT64:
|
||||
case VLVT_WDATA: {
|
||||
int bits = vop->varp()->range().bits();
|
||||
int bits = vop->varp()->range().elements();
|
||||
int len = strlen(value_p->value.str);
|
||||
CData* datap = ((CData*)(vop->varDatap()));
|
||||
for (int i=0; i<bits; i++) {
|
||||
@@ -1040,8 +1110,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
||||
case VLVT_UINT32:
|
||||
case VLVT_UINT64:
|
||||
case VLVT_WDATA: {
|
||||
int chars = (vop->varp()->range().bits()+2)/3;
|
||||
int bytes = VL_BYTES_I(vop->varp()->range().bits());
|
||||
int chars = (vop->varp()->range().elements()+2)/3;
|
||||
int bytes = VL_BYTES_I(vop->varp()->range().elements());
|
||||
int len = strlen(value_p->value.str);
|
||||
CData* datap = ((CData*)(vop->varDatap()));
|
||||
div_t idx;
|
||||
@@ -1096,8 +1166,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
||||
}
|
||||
} else if (value_p->format == vpiDecStrVal) {
|
||||
char remainder[16];
|
||||
unsigned long val;
|
||||
int success = sscanf(value_p->value.str, "%30lu%15s", &val, remainder);
|
||||
unsigned long long val;
|
||||
int success = sscanf(value_p->value.str, "%30llu%15s", &val, remainder);
|
||||
if (success < 1) {
|
||||
_VL_VPI_ERROR(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s",
|
||||
VL_FUNC, value_p->value.str, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname());
|
||||
@@ -1126,7 +1196,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
||||
case VLVT_UINT32:
|
||||
case VLVT_UINT64:
|
||||
case VLVT_WDATA: {
|
||||
int chars = (vop->varp()->range().bits()+3)>>2;
|
||||
int chars = (vop->varp()->range().elements()+3)>>2;
|
||||
CData* datap = ((CData*)(vop->varDatap()));
|
||||
char* val = value_p->value.str;
|
||||
// skip hex ident if one is detected at the start of the string
|
||||
@@ -1173,7 +1243,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p,
|
||||
case VLVT_UINT32:
|
||||
case VLVT_UINT64:
|
||||
case VLVT_WDATA: {
|
||||
int bytes = VL_BYTES_I(vop->varp()->range().bits());
|
||||
int bytes = VL_BYTES_I(vop->varp()->range().elements());
|
||||
int len = strlen(value_p->value.str);
|
||||
CData* datap = ((CData*)(vop->varDatap()));
|
||||
for (int i=0; i<bytes; i++) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* This file contains the constant definitions, structure definitions,
|
||||
* and routine declarations used by SystemVerilog DPI.
|
||||
*
|
||||
* This file is from the SystemVerilog IEEE 1800-2009 Annex I.
|
||||
* This file is from the SystemVerilog IEEE 1800-2012 Annex I.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_SVDPI
|
||||
@@ -35,18 +35,18 @@ typedef signed __int8 int8_t;
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
/* Use to export a symbol from application */
|
||||
/* Use to import a symbol into dll */
|
||||
#ifndef DPI_DLLISPEC
|
||||
#if defined (_MSC_VER)
|
||||
#if (defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__))
|
||||
#define DPI_DLLISPEC __declspec(dllimport)
|
||||
#else
|
||||
#define DPI_DLLISPEC
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Use to import a symbol into application */
|
||||
/* Use to export a symbol from dll */
|
||||
#ifndef DPI_DLLESPEC
|
||||
#if defined (_MSC_VER)
|
||||
#if (defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__))
|
||||
#define DPI_DLLESPEC __declspec(dllexport)
|
||||
#else
|
||||
#define DPI_DLLESPEC
|
||||
|
||||
+21
-21
@@ -1,7 +1,7 @@
|
||||
/*******************************************************************************
|
||||
* vpi_user.h
|
||||
*
|
||||
* IEEE Std 1800 Programming Language Interface (PLI)
|
||||
* IEEE Std 1800-2012 Programming Language Interface (PLI)
|
||||
*
|
||||
* This file contains the constant definitions, structure definitions, and
|
||||
* routine declarations used by the SystemVerilog Verification Procedural
|
||||
@@ -61,9 +61,9 @@ typedef char PLI_BYTE8;
|
||||
typedef unsigned char PLI_UBYTE8;
|
||||
#endif
|
||||
|
||||
/* Use to export a symbol */
|
||||
/* Use to import a symbol */
|
||||
|
||||
#if WIN32
|
||||
#if (defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__))
|
||||
#ifndef PLI_DLLISPEC
|
||||
#define PLI_DLLISPEC __declspec(dllimport)
|
||||
#define VPI_USER_DEFINED_DLLISPEC 1
|
||||
@@ -74,9 +74,9 @@ typedef unsigned char PLI_UBYTE8;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Use to import a symbol */
|
||||
/* Use to export a symbol */
|
||||
|
||||
#if WIN32
|
||||
#if (defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__))
|
||||
#ifndef PLI_DLLESPEC
|
||||
#define PLI_DLLESPEC __declspec(dllexport)
|
||||
#define VPI_USER_DEFINED_DLLESPEC 1
|
||||
@@ -438,12 +438,12 @@ typedef PLI_UINT32 *vpiHandle;
|
||||
#define vpiPlusOp 2 /* unary plus */
|
||||
#define vpiNotOp 3 /* unary not */
|
||||
#define vpiBitNegOp 4 /* bitwise negation */
|
||||
#define vpiUnaryAndOp 5 /* bitwise reduction and */
|
||||
#define vpiUnaryNandOp 6 /* bitwise reduction nand */
|
||||
#define vpiUnaryOrOp 7 /* bitwise reduction or */
|
||||
#define vpiUnaryNorOp 8 /* bitwise reduction nor */
|
||||
#define vpiUnaryXorOp 9 /* bitwise reduction xor */
|
||||
#define vpiUnaryXNorOp 10 /* bitwise reduction xnor */
|
||||
#define vpiUnaryAndOp 5 /* bitwise reduction AND */
|
||||
#define vpiUnaryNandOp 6 /* bitwise reduction NAND */
|
||||
#define vpiUnaryOrOp 7 /* bitwise reduction OR */
|
||||
#define vpiUnaryNorOp 8 /* bitwise reduction NOR */
|
||||
#define vpiUnaryXorOp 9 /* bitwise reduction XOR */
|
||||
#define vpiUnaryXNorOp 10 /* bitwise reduction XNOR */
|
||||
#define vpiSubOp 11 /* binary subtraction */
|
||||
#define vpiDivOp 12 /* binary division */
|
||||
#define vpiModOp 13 /* binary modulus */
|
||||
@@ -459,17 +459,17 @@ typedef PLI_UINT32 *vpiHandle;
|
||||
#define vpiRShiftOp 23 /* binary right shift */
|
||||
#define vpiAddOp 24 /* binary addition */
|
||||
#define vpiMultOp 25 /* binary multiplication */
|
||||
#define vpiLogAndOp 26 /* binary logical and */
|
||||
#define vpiLogOrOp 27 /* binary logical or */
|
||||
#define vpiBitAndOp 28 /* binary bitwise and */
|
||||
#define vpiBitOrOp 29 /* binary bitwise or */
|
||||
#define vpiBitXorOp 30 /* binary bitwise xor */
|
||||
#define vpiBitXNorOp 31 /* binary bitwise xnor */
|
||||
#define vpiLogAndOp 26 /* binary logical AND */
|
||||
#define vpiLogOrOp 27 /* binary logical OR */
|
||||
#define vpiBitAndOp 28 /* binary bitwise AND */
|
||||
#define vpiBitOrOp 29 /* binary bitwise OR */
|
||||
#define vpiBitXorOp 30 /* binary bitwise XOR */
|
||||
#define vpiBitXNorOp 31 /* binary bitwise XNOR */
|
||||
#define vpiBitXnorOp vpiBitXNorOp /* added with 1364-2001 */
|
||||
#define vpiConditionOp 32 /* ternary conditional */
|
||||
#define vpiConcatOp 33 /* n-ary concatenation */
|
||||
#define vpiMultiConcatOp 34 /* repeated concatenation */
|
||||
#define vpiEventOrOp 35 /* event or */
|
||||
#define vpiEventOrOp 35 /* event OR */
|
||||
#define vpiNullOp 36 /* null operation */
|
||||
#define vpiListOp 37 /* list of expressions */
|
||||
#define vpiMinTypMaxOp 38 /* min:typ:max: delay expression */
|
||||
@@ -536,7 +536,7 @@ typedef PLI_UINT32 *vpiHandle;
|
||||
same subtypes as vpiNetType */
|
||||
#define vpiSaveRestartID 62 /* unique ID for save/restart data */
|
||||
#define vpiSaveRestartLocation 63 /* name of save/restart data file */
|
||||
/* vpiValid,vpiValidTrue,vpiValidFalse are deprecated in 1800-2009 */
|
||||
/* vpiValid,vpiValidTrue,vpiValidFalse were deprecated in 1800-2009 */
|
||||
#define vpiValid 64 /* reentrant task/func frame or automatic
|
||||
variable is valid */
|
||||
#define vpiValidFalse 0
|
||||
@@ -661,7 +661,7 @@ typedef struct t_vpi_arrayvalue
|
||||
{
|
||||
PLI_INT32 *integers; /* integer values */
|
||||
PLI_INT16 *shortints; /* short integer values */
|
||||
PLI_INT16 *longints; /* long integer values */
|
||||
PLI_INT64 *longints; /* long integer values */
|
||||
PLI_BYTE8 *rawvals; /* 2/4-state vector elements */
|
||||
struct t_vpi_vecval *vectors; /* 4-state vector elements */
|
||||
struct t_vpi_time *times; /* time values */
|
||||
@@ -944,7 +944,7 @@ XXTERN PLI_INT32 vpi_compare_objects PROTO_PARAMS((vpiHandle object1,
|
||||
vpiHandle object2));
|
||||
XXTERN PLI_INT32 vpi_chk_error PROTO_PARAMS((p_vpi_error_info
|
||||
error_info_p));
|
||||
/* vpi_free_object() is deprecated in 1800-2009 */
|
||||
/* vpi_free_object() was deprecated in 1800-2009 */
|
||||
XXTERN PLI_INT32 vpi_free_object PROTO_PARAMS((vpiHandle object));
|
||||
XXTERN PLI_INT32 vpi_release_handle PROTO_PARAMS((vpiHandle object));
|
||||
XXTERN PLI_INT32 vpi_get_vlog_info PROTO_PARAMS((p_vpi_vlog_info
|
||||
|
||||
+56
-5
@@ -366,10 +366,8 @@ AST.
|
||||
|
||||
=head1 TESTING
|
||||
|
||||
To write a test see the BUGS section of the Verilator primary manual, and
|
||||
the documentation in:
|
||||
|
||||
test_regress/t/driver.pl --help
|
||||
For an overview of how to write a test see the BUGS section of the
|
||||
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).
|
||||
@@ -378,7 +376,60 @@ Tests that fail should by convenition 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.
|
||||
|
||||
Developers will also want to configure with two extra flags:
|
||||
=head2 Controlling the Test Driver
|
||||
|
||||
Test drivers are written in PERL. All invoke the main test driver script,
|
||||
which can provide detailed help on all the features available when writing
|
||||
a test driver.
|
||||
|
||||
test_regress/t/driver.pl --help
|
||||
|
||||
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.
|
||||
|
||||
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>
|
||||
will expect a Verilog source file C<t/t_mytest.v>. This can be changed
|
||||
using the C<top_filename> subroutine, for example
|
||||
|
||||
top_filename("t/t_myothertest.v");
|
||||
|
||||
By default all tests will run with major simulators (Icarus Verilog, NC,
|
||||
VCS, ModelSim) as well as Verilator, to allow results to be
|
||||
compared. However if you wish a test only to be used with Verilator, you
|
||||
can use the following:
|
||||
|
||||
$Self->{vlt} or $Self->skip("Verilator only test");
|
||||
|
||||
Of the many options that can be set through arguments to C<compiler> and
|
||||
C<execute>, the following are particularly useful:
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<verilator_flags2>
|
||||
|
||||
A list of flags to be passed to verilator when compiling.
|
||||
|
||||
=item C<fails>
|
||||
|
||||
Set to 1 to indicate that the compilation or execution is intended to fail.
|
||||
|
||||
=back
|
||||
|
||||
For example the following would specify that compilation requires two
|
||||
defines and is expected to fail.
|
||||
|
||||
compile (
|
||||
verilator_flags2 => ["-DSMALL_CLOCK -DGATED_COMMENT"],
|
||||
fails => 1,
|
||||
);
|
||||
|
||||
=head2 Regression Testing for Developers
|
||||
|
||||
Developers will also want to call ./configure with two extra flags:
|
||||
|
||||
=over 4
|
||||
|
||||
|
||||
+19
-23
@@ -24,14 +24,14 @@ more details.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Verilator converts synthesizable (not behavioral) Verilog code into C++ or
|
||||
SystemC code. It is not a complete simulator, just a translator.
|
||||
Verilator converts synthesizable (generally not behavioral) Verilog code
|
||||
into C++ or SystemC code. It is not a complete simulator, just a
|
||||
translator.
|
||||
|
||||
Verilator is invoked with parameters similar to GCC or Synopsys's VCS. It
|
||||
reads the specified Verilog code, lints it, and optionally adds coverage
|
||||
code. For C++ format, it outputs .cpp and .h files. For SystemC format,
|
||||
it outputs .sp files for the SystemPerl preprocessor available at
|
||||
http://www.veripool.org.
|
||||
it outputs .cpp and .h files using the standard SystemC headers.
|
||||
|
||||
The resulting files are then compiled with C++. The user writes a little
|
||||
C++ wrapper file, which instantiates the top level module. This is
|
||||
@@ -41,14 +41,11 @@ The resulting executable will perform the actual simulation.
|
||||
|
||||
=head1 SUPPORTED SYSTEMS
|
||||
|
||||
Verilator is developed and has primary testing on:
|
||||
|
||||
SuSE 11.1 AMD64 i686-linux-2.6.27, GCC 4.3.2
|
||||
|
||||
Versions have also built on Redhat Linux, Macs OS-X, HPUX and Solaris. It
|
||||
should run with minor porting on any Linix-ish platform. Verilator also
|
||||
works on Windows under Cygwin, and Windows under MinGW (gcc -mno-cygwin).
|
||||
Verilated output (not Verilator itself) compiles under MSVC++ 2008.
|
||||
Verilator is developed and has primary testing on Ubuntu. Versions have
|
||||
also built on Redhat Linux, Macs OS-X, HPUX and Solaris. It should run
|
||||
with minor porting on any Linix-ish platform. Verilator also works on
|
||||
Windows under Cygwin, and Windows under MinGW (gcc -mno-cygwin). Verilated
|
||||
output (not Verilator itself) compiles under MSVC++ 2008 and newer.
|
||||
|
||||
=head1 INSTALLATION
|
||||
|
||||
@@ -71,11 +68,11 @@ Download the latest package from that site, and decompress.
|
||||
=item
|
||||
|
||||
If you will be using SystemC (vs straight C++ output), download SystemC
|
||||
2.0.1 from L<http://www.systemc.org>. Follow their installation
|
||||
instructions. You will need to set SYSTEMC_INCLUDE to point to the
|
||||
include directory with systemc.h in it, and SYSTEMC_LIBDIR to points
|
||||
to the directory with libsystemc.a in it. (Older installations may
|
||||
set SYSTEMC and SYSTEMC_ARCH instead.)
|
||||
from L<http://www.systemc.org>. Follow their installation instructions.
|
||||
You will need to set SYSTEMC_INCLUDE to point to the include directory with
|
||||
systemc.h in it, and SYSTEMC_LIBDIR to points to the directory with
|
||||
libsystemc.a in it. (Older installations may set SYSTEMC and SYSTEMC_ARCH
|
||||
instead.)
|
||||
|
||||
=item
|
||||
|
||||
@@ -107,7 +104,7 @@ executable, so try to have them correct before configuring.
|
||||
|
||||
Our personal favorite is to always run Verilator from the kit directory.
|
||||
This allows the easiest experimentation and upgrading. It's also how most
|
||||
EDA tools operate; to run any of them you point to the tarball.
|
||||
EDA tools operate; to run you point to the tarball, no install is needed.
|
||||
|
||||
export VERILATOR_ROOT=`pwd` # if your shell is bash
|
||||
setenv VERILATOR_ROOT `pwd` # if your shell is csh
|
||||
@@ -156,11 +153,10 @@ PATH.
|
||||
|
||||
Type C<make> to compile Verilator.
|
||||
|
||||
Type C<make test_c> to check the compilation.
|
||||
Type C<make test> to check the compilation.
|
||||
|
||||
Type C<make test> for a more complete test. You may get a error about the
|
||||
Bit::Vector Perl package. You will need to install it and SystemPerl if
|
||||
you want all tests to pass.
|
||||
Configure with C<--enable-longtests> for more complete developer tests.
|
||||
Additional packages may be required for these tests.
|
||||
|
||||
You may get a error about a typedef conflict for uint32_t. Edit
|
||||
verilated.h to change the typedef to work, probably to @samp{typedef
|
||||
@@ -195,7 +191,7 @@ The directories in the kit after de-taring are as follows:
|
||||
bin/verilator => Compiler Wrapper invoked to Verilate code
|
||||
include/ => Files that should be in your -I compiler path
|
||||
include/verilated*.cpp => Global routines to link into your simulator
|
||||
include/verilated.h => Global headers
|
||||
include/verilated*.h => Global headers
|
||||
include/verilated.v => Stub defines for linting
|
||||
include/verilated.mk => Common makefile
|
||||
src/ => Translator source code
|
||||
|
||||
+1
-1
@@ -291,7 +291,7 @@ private:
|
||||
}
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
if (!m_scopeFinalp) {
|
||||
m_scopeFinalp = new AstCFunc(nodep->fileline(), "_final", m_namer.scopep());
|
||||
m_scopeFinalp = new AstCFunc(nodep->fileline(), "_final_"+m_namer.scopep()->nameDotless(), m_namer.scopep());
|
||||
m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_scopeFinalp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
|
||||
m_scopeFinalp->dontCombine(true);
|
||||
|
||||
@@ -137,6 +137,7 @@ private:
|
||||
if (nodep->castPslAssert()) ifp->branchPred(AstBranchPred::BP_UNLIKELY);
|
||||
//
|
||||
AstNode* newp = new AstAlways (nodep->fileline(),
|
||||
VAlwaysKwd::ALWAYS,
|
||||
sentreep,
|
||||
bodysp);
|
||||
// Install it
|
||||
|
||||
@@ -965,6 +965,14 @@ void AstNode::dumpTreeFileGdb(const char* filenamep) { // For GDB only
|
||||
v3Global.rootp()->dumpTreeFile(filename);
|
||||
}
|
||||
|
||||
void AstNode::checkIter() const {
|
||||
if (m_iterpp) {
|
||||
dumpPtrs(cout);
|
||||
// Perhaps something forgot to clear m_iterpp?
|
||||
this->v3fatalSrc("Iteration link should be NULL");
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::dumpPtrs(ostream& os) const {
|
||||
os<<"This="<<typeName()<<" "<<(void*)this;
|
||||
os<<" back="<<(void*)backp();
|
||||
|
||||
+46
-8
@@ -38,6 +38,9 @@ class VFlagLogicPacked {};
|
||||
class VFlagBitPacked {};
|
||||
class VFlagChildDType {}; // Used by parser.y to select constructor that sets childDType
|
||||
|
||||
// For broken() function, return error string if have a match
|
||||
#define BROKEN_RTN(test) do { if (VL_UNLIKELY(test)) return # test; } while(0)
|
||||
|
||||
//######################################################################
|
||||
|
||||
class AstType {
|
||||
@@ -407,7 +410,8 @@ public:
|
||||
BLOCKTEMP,
|
||||
MODULETEMP,
|
||||
STMTTEMP,
|
||||
XTEMP
|
||||
XTEMP,
|
||||
IFACEREF // Used to link Interfaces between modules
|
||||
};
|
||||
enum en m_e;
|
||||
inline AstVarType () : m_e(UNKNOWN) {}
|
||||
@@ -421,7 +425,8 @@ public:
|
||||
"SUPPLY0","SUPPLY1","WIRE","IMPLICITWIRE",
|
||||
"TRIWIRE","TRI0","TRI1",
|
||||
"PORT",
|
||||
"BLOCKTEMP","MODULETEMP","STMTTEMP","XTEMP"};
|
||||
"BLOCKTEMP","MODULETEMP","STMTTEMP","XTEMP",
|
||||
"IFACEREF"};
|
||||
return names[m_e]; }
|
||||
bool isSignal() const { return (m_e==WIRE || m_e==IMPLICITWIRE
|
||||
|| m_e==TRIWIRE
|
||||
@@ -467,6 +472,30 @@ public:
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VAlwaysKwd {
|
||||
public:
|
||||
enum en {
|
||||
ALWAYS,
|
||||
ALWAYS_FF,
|
||||
ALWAYS_LATCH,
|
||||
ALWAYS_COMB
|
||||
};
|
||||
enum en m_e;
|
||||
inline VAlwaysKwd () : m_e(ALWAYS) {}
|
||||
inline VAlwaysKwd (en _e) : m_e(_e) {}
|
||||
explicit inline VAlwaysKwd (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* names[] = {
|
||||
"always","always_ff","always_latch","always_comb"};
|
||||
return names[m_e]; }
|
||||
};
|
||||
inline bool operator== (VAlwaysKwd lhs, VAlwaysKwd rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (VAlwaysKwd lhs, VAlwaysKwd::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (VAlwaysKwd::en lhs, VAlwaysKwd rhs) { return (lhs == rhs.m_e); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
class AstCaseType {
|
||||
public:
|
||||
enum en {
|
||||
@@ -1124,6 +1153,8 @@ public:
|
||||
bool sameTree(AstNode* node2p); // Does tree of this == node2p?
|
||||
void deleteTree(); // Always deletes the next link
|
||||
void checkTree(); // User Interface version
|
||||
void checkIter() const;
|
||||
void clearIter() { m_iterpp=NULL; }
|
||||
void dumpPtrs(ostream& str=cout) const;
|
||||
void dumpTree(ostream& str=cout, const string& indent=" ", int maxDepth=0);
|
||||
void dumpTree(const string& indent, int maxDepth=0) { dumpTree(cout,indent,maxDepth); }
|
||||
@@ -1136,6 +1167,7 @@ public:
|
||||
virtual bool isPure() const { return true; } // Else a $display, etc, that must be ordered with other displays
|
||||
virtual bool isBrancher() const { return false; } // Changes control flow, disable some optimizations
|
||||
virtual bool isGateOptimizable() const { return true; } // Else a AstTime etc that can't be pushed out
|
||||
virtual bool isGateDedupable() const { return isGateOptimizable(); } // GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf)
|
||||
virtual bool isSubstOptimizable() const { return true; } // Else a AstTime etc that can't be substituted out
|
||||
virtual bool isPredictOptimizable() const { return true; } // Else a AstTime etc which output can't be predicted from input
|
||||
virtual bool isOutputter() const { return false; } // Else creates output or exits, etc, not unconsumed
|
||||
@@ -1146,7 +1178,7 @@ public:
|
||||
virtual bool hasDType() const { return false; } // Iff has a data type; dtype() must be non null
|
||||
virtual AstNodeDType* getChildDTypep() const { return NULL; } // Iff has a non-null childDTypep(), as generic node function
|
||||
virtual bool maybePointedTo() const { return false; } // Another AstNode* may have a pointer into this node, other then normal front/back/etc.
|
||||
virtual bool broken() const { return false; }
|
||||
virtual const char* broken() const { return NULL; }
|
||||
|
||||
// INVOKERS
|
||||
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) = 0;
|
||||
@@ -1389,6 +1421,7 @@ public:
|
||||
void addIfsp(AstNode* newp) { addOp2p(newp); }
|
||||
void addElsesp(AstNode* newp) { addOp3p(newp); }
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
virtual bool isGateDedupable() const { return true; }
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
@@ -1446,7 +1479,7 @@ public:
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeVarRef)
|
||||
virtual bool hasDType() const { return true; }
|
||||
virtual bool broken() const;
|
||||
virtual const char* broken() const;
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
virtual void cloneRelink();
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
@@ -1553,7 +1586,7 @@ public:
|
||||
numeric(numericUnpack.isSigned() ? AstNumeric::SIGNED : AstNumeric::UNSIGNED);
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeClassDType)
|
||||
virtual bool broken() const;
|
||||
virtual const char* broken() const;
|
||||
virtual void dump(ostream& str);
|
||||
// For basicp() we reuse the size to indicate a "fake" basic type of same size
|
||||
virtual AstBasicDType* basicp() const { return findLogicDType(width(),width(),numeric())->castBasicDType(); }
|
||||
@@ -1589,8 +1622,8 @@ public:
|
||||
ASTNODE_BASE_FUNCS(NodeArrayDType)
|
||||
virtual void dump(ostream& str);
|
||||
virtual void dumpSmall(ostream& str);
|
||||
virtual bool broken() const { return !((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep())); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep()))); return NULL; }
|
||||
virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) {
|
||||
m_refDTypep = m_refDTypep->clonep()->castNodeDType();
|
||||
}}
|
||||
@@ -1714,7 +1747,7 @@ public:
|
||||
addNOp2p(pinsp);
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeFTaskRef)
|
||||
virtual bool broken() const { return m_taskp && !m_taskp->brokeExists(); }
|
||||
virtual const char* broken() const { BROKEN_RTN(m_taskp && !m_taskp->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_taskp && m_taskp->clonep()) {
|
||||
m_taskp = m_taskp->clonep()->castNodeFTask();
|
||||
}}
|
||||
@@ -1821,4 +1854,9 @@ inline int AstNodeArrayDType::lsb() const { return rangep()->lsbConst(); }
|
||||
inline int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
|
||||
inline VNumRange AstNodeArrayDType::declRange() const { return VNumRange(msb(), lsb(), rangep()->littleEndian()); }
|
||||
|
||||
inline void AstIfaceRefDType::cloneRelink() {
|
||||
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep()->castCell();
|
||||
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep()->castIface();
|
||||
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep()->castModport(); }
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+69
-17
@@ -35,8 +35,22 @@
|
||||
// Special methods
|
||||
|
||||
// We need these here, because the classes they point to aren't defined when we declare the class
|
||||
bool AstNodeVarRef::broken() const { return ((m_varScopep && !m_varScopep->brokeExists())
|
||||
|| (m_varp && !m_varp->brokeExists())); }
|
||||
const char* AstIfaceRefDType::broken() const {
|
||||
BROKEN_RTN(m_ifacep && !m_ifacep->brokeExists());
|
||||
BROKEN_RTN(m_cellp && !m_cellp->brokeExists());
|
||||
BROKEN_RTN(m_modportp && !m_modportp->brokeExists());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AstIface* AstIfaceRefDType::ifaceViaCellp() const {
|
||||
return ((m_cellp && m_cellp->modp()) ? m_cellp->modp()->castIface() : m_ifacep);
|
||||
}
|
||||
|
||||
const char* AstNodeVarRef::broken() const {
|
||||
BROKEN_RTN(m_varScopep && !m_varScopep->brokeExists());
|
||||
BROKEN_RTN(m_varp && !m_varp->brokeExists());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void AstNodeVarRef::cloneRelink() {
|
||||
if (m_varp && m_varp->clonep()) { m_varp = m_varp->clonep()->castVar(); }
|
||||
@@ -54,18 +68,18 @@ void AstNodeClassDType::repairMemberCache() {
|
||||
}
|
||||
}
|
||||
|
||||
bool AstNodeClassDType::broken() const {
|
||||
const char* AstNodeClassDType::broken() const {
|
||||
set<AstMemberDType*> exists;
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) {
|
||||
exists.insert(itemp);
|
||||
}
|
||||
for (MemberNameMap::const_iterator it=m_members.begin(); it!=m_members.end(); ++it) {
|
||||
if (exists.find(it->second) == exists.end()) {
|
||||
if (VL_UNLIKELY(exists.find(it->second) == exists.end())) {
|
||||
this->v3error("Internal: Structure member broken: "<<it->first);
|
||||
return true;
|
||||
return "member broken";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int AstBasicDType::widthAlignBytes() const {
|
||||
@@ -103,13 +117,21 @@ bool AstVar::isSigPublic() const {
|
||||
}
|
||||
|
||||
bool AstVar::isScQuad() const {
|
||||
return (isSc() && isQuad() && !isScBv());
|
||||
return (isSc() && isQuad() && !isScBv() && !isScBigUint());
|
||||
}
|
||||
|
||||
bool AstVar::isScBv() const {
|
||||
return ((isSc() && width() >= v3Global.opt.pinsBv()) || m_attrScBv);
|
||||
}
|
||||
|
||||
bool AstVar::isScUint() const {
|
||||
return ((isSc() && v3Global.opt.pinsScUint() && width() >= 2 && width() <= 64) && !isScBv());
|
||||
}
|
||||
|
||||
bool AstVar::isScBigUint() const {
|
||||
return ((isSc() && v3Global.opt.pinsScBigUint() && width() >= 65 && width() <= 512) && !isScBv());
|
||||
}
|
||||
|
||||
void AstVar::combineType(AstVarType type) {
|
||||
// These flags get combined with the existing settings of the flags.
|
||||
// We don't test varType for certain types, instead set flags since
|
||||
@@ -278,7 +300,11 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
|
||||
}
|
||||
|
||||
string AstVar::scType() const {
|
||||
if (isScBv()) {
|
||||
if (isScBigUint()) {
|
||||
return (string("sc_biguint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
|
||||
} else if (isScUint()) {
|
||||
return (string("sc_uint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
|
||||
} else if (isScBv()) {
|
||||
return (string("sc_bv<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
|
||||
} else if (widthMin() == 1) {
|
||||
return "bool";
|
||||
@@ -407,10 +433,12 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep) { ///< What is the base variable
|
||||
return nodep;
|
||||
}
|
||||
|
||||
bool AstScope::broken() const {
|
||||
return ((m_aboveScopep && !m_aboveScopep->brokeExists())
|
||||
|| (m_aboveCellp && !m_aboveCellp->brokeExists())
|
||||
|| !m_modp || !m_modp->brokeExists());
|
||||
const char* AstScope::broken() const {
|
||||
BROKEN_RTN(m_aboveScopep && !m_aboveScopep->brokeExists());
|
||||
BROKEN_RTN(m_aboveCellp && !m_aboveCellp->brokeExists());
|
||||
BROKEN_RTN(!m_modp);
|
||||
BROKEN_RTN(m_modp && !m_modp->brokeExists());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void AstScope::cloneRelink() {
|
||||
@@ -665,6 +693,11 @@ void AstNode::dump(ostream& str) {
|
||||
if (name()!="") str<<" "<<AstNode::quoteName(name());
|
||||
}
|
||||
|
||||
void AstAlways::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (keyword() != VAlwaysKwd::ALWAYS) str<<" ["<<keyword().ascii()<<"]";
|
||||
}
|
||||
|
||||
void AstArraySel::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" [start:"<<start()<<"] [length:"<<length()<<"]";
|
||||
@@ -695,17 +728,36 @@ void AstDisplay::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
//str<<" "<<displayType().ascii();
|
||||
}
|
||||
void AstEnumItemRef::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (itemp()) { itemp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstIfaceRefDType::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (cellName()!="") { str<<" cell="<<cellName(); }
|
||||
if (ifaceName()!="") { str<<" if="<<ifaceName(); }
|
||||
if (modportName()!="") { str<<" mp="<<modportName(); }
|
||||
if (cellp()) { str<<" -> "; cellp()->dump(str); }
|
||||
else if (ifacep()) { str<<" -> "; ifacep()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstIfaceRefDType::dumpSmall(ostream& str) {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"iface";
|
||||
}
|
||||
void AstJumpGo::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (labelp()) { labelp()->dump(str); }
|
||||
else { str<<"%Error:UNLINKED"; }
|
||||
}
|
||||
void AstEnumItemRef::dump(ostream& str) {
|
||||
void AstModportVarRef::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (itemp()) { itemp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
str<<" "<<varType();
|
||||
if (varp()) { str<<" -> "; varp()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstPin::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
@@ -822,7 +874,7 @@ void AstVarXRef::dump(ostream& str) {
|
||||
if (lvalue()) str<<" [LV] => ";
|
||||
else str<<" [RV] <- ";
|
||||
str<<dotted()<<". - ";
|
||||
if (inlinedDots()!="") str<<" flat.="<<inlinedDots()<<" - ";
|
||||
if (inlinedDots()!="") str<<" inline.="<<inlinedDots()<<" - ";
|
||||
if (varScopep()) { varScopep()->dump(str); }
|
||||
else if (varp()) { varp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
|
||||
+195
-43
@@ -244,16 +244,16 @@ public:
|
||||
refDTypep(NULL);
|
||||
setOp2p(rangep);
|
||||
dtypep(NULL); // V3Width will resolve
|
||||
// For backward compatibility AstNodeArrayDType and others inherit width and signing from the subDType/base type
|
||||
widthFromSub(subDTypep());
|
||||
int width = subDTypep()->width() * rangep->elementsConst();
|
||||
widthForce(width,width);
|
||||
}
|
||||
AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep)
|
||||
: AstNodeArrayDType(fl) {
|
||||
refDTypep(dtp);
|
||||
setOp2p(rangep);
|
||||
dtypep(this);
|
||||
// For backward compatibility AstNodeArrayDType and others inherit width and signing from the subDType/base type
|
||||
widthFromSub(subDTypep());
|
||||
int width = subDTypep()->width() * rangep->elementsConst();
|
||||
widthForce(width,width);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(PackArrayDType, PACKARRAYDTYPE)
|
||||
};
|
||||
@@ -357,7 +357,7 @@ public:
|
||||
virtual bool same(AstNode* samep) const { // width/widthMin/numeric compared elsewhere
|
||||
return samep->castBasicDType()->m == m; }
|
||||
virtual string name() const { return m.m_keyword.ascii(); }
|
||||
virtual bool broken() const { return dtypep()!=this; }
|
||||
virtual const char* broken() const { BROKEN_RTN(dtypep()!=this); return NULL; }
|
||||
AstRange* rangep() const { return op1p()->castRange(); } // op1 = Range of variable
|
||||
void rangep(AstRange* nodep) { setNOp1p(nodep); }
|
||||
void setSignedState(VSignedState signst) {
|
||||
@@ -410,8 +410,8 @@ public:
|
||||
widthFromSub(subDTypep());
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(ConstDType, CONSTDTYPE)
|
||||
virtual bool broken() const { return !((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep())); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep()))); return NULL; }
|
||||
virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) {
|
||||
m_refDTypep = m_refDTypep->clonep()->castNodeDType();
|
||||
}}
|
||||
@@ -432,6 +432,48 @@ public:
|
||||
virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); }
|
||||
};
|
||||
|
||||
struct AstIfaceRefDType : public AstNodeDType {
|
||||
// Reference to an interface, either for a port, or inside parent cell
|
||||
private:
|
||||
string m_cellName; // "" = no cell, such as when connects to 'input' iface
|
||||
string m_ifaceName; // Interface name
|
||||
string m_modportName; // "" = no modport
|
||||
AstIface* m_ifacep; // Pointer to interface; note cellp() should override
|
||||
AstCell* m_cellp; // When exact parent cell known; not a guess
|
||||
AstModport* m_modportp; // NULL = unlinked or no modport
|
||||
public:
|
||||
AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName)
|
||||
: AstNodeDType(fl), m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(""),
|
||||
m_ifacep(NULL), m_cellp(NULL), m_modportp(NULL) { }
|
||||
AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName, const string& modport)
|
||||
: AstNodeDType(fl), m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(modport),
|
||||
m_ifacep(NULL), m_cellp(NULL), m_modportp(NULL) { }
|
||||
ASTNODE_NODE_FUNCS(IfaceRefDType, IFACEREFDTYPE)
|
||||
// METHODS
|
||||
virtual const char* broken() const;
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual void dumpSmall(ostream& str);
|
||||
virtual void cloneRelink();
|
||||
virtual AstBasicDType* basicp() const { return NULL; }
|
||||
virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; }
|
||||
virtual int widthAlignBytes() const { return 1; }
|
||||
virtual int widthTotalBytes() const { return 1; }
|
||||
string cellName() const { return m_cellName; }
|
||||
void cellName(const string& name) { m_cellName=name; }
|
||||
string ifaceName() const { return m_ifaceName; }
|
||||
void ifaceName(const string& name) { m_ifaceName=name; }
|
||||
string modportName() const { return m_modportName; }
|
||||
void modportName(const string& name) { m_modportName=name; }
|
||||
AstIface* ifaceViaCellp() const; // Use cellp or ifacep
|
||||
AstIface* ifacep() const { return m_ifacep; }
|
||||
void ifacep(AstIface* nodep) { m_ifacep=nodep; }
|
||||
AstCell* cellp() const { return m_cellp; }
|
||||
void cellp(AstCell* nodep) { m_cellp=nodep; }
|
||||
AstModport* modportp() const { return m_modportp; }
|
||||
void modportp(AstModport* modportp) { m_modportp=modportp; }
|
||||
bool isModport() { return !m_modportName.empty(); }
|
||||
};
|
||||
|
||||
struct AstRefDType : public AstNodeDType {
|
||||
private:
|
||||
AstNodeDType* m_refDTypep; // data type pointed to, BELOW the AstTypedef
|
||||
@@ -447,7 +489,7 @@ public:
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(RefDType, REFDTYPE)
|
||||
// METHODS
|
||||
virtual bool broken() const { return m_refDTypep && !m_refDTypep->brokeExists(); }
|
||||
virtual const char* broken() const { BROKEN_RTN(m_refDTypep && !m_refDTypep->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) {
|
||||
m_refDTypep = m_refDTypep->clonep()->castNodeDType();
|
||||
}}
|
||||
@@ -571,7 +613,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(EnumItemRef, ENUMITEMREF)
|
||||
virtual void dump(ostream& str);
|
||||
virtual string name() const { return itemp()->name(); }
|
||||
virtual bool broken() const { return !itemp(); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!itemp()); return NULL; }
|
||||
virtual int instrCount() const { return 0; }
|
||||
virtual void cloneRelink() { if (m_itemp->clonep()) m_itemp = m_itemp->clonep()->castEnumItem(); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
@@ -601,8 +643,8 @@ public:
|
||||
m_uniqueNum = uniqueNumInc();
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(EnumDType, ENUMDTYPE)
|
||||
virtual bool broken() const { return !((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep())); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep()))); return NULL; }
|
||||
virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) {
|
||||
m_refDTypep = m_refDTypep->clonep()->castNodeDType();
|
||||
}}
|
||||
@@ -815,6 +857,7 @@ struct AstVar : public AstNode {
|
||||
// A variable (in/out/wire/reg/param) inside a module
|
||||
private:
|
||||
string m_name; // Name of variable
|
||||
string m_origName; // Original name before dot addition
|
||||
AstVarType m_varType; // Type of variable
|
||||
bool m_input:1; // Input or inout
|
||||
bool m_output:1; // Output or inout
|
||||
@@ -842,6 +885,7 @@ private:
|
||||
bool m_isStatic:1; // Static variable
|
||||
bool m_isPulldown:1; // Tri0
|
||||
bool m_isPullup:1; // Tri1
|
||||
bool m_isIfaceParent:1; // dtype is reference to interface present in this module
|
||||
bool m_trace:1; // Trace this variable
|
||||
|
||||
void init() {
|
||||
@@ -853,12 +897,13 @@ private:
|
||||
m_funcLocal=false; m_funcReturn=false;
|
||||
m_attrClockEn=false; m_attrScBv=false; m_attrIsolateAssign=false; m_attrSFormat=false;
|
||||
m_fileDescr=false; m_isConst=false; m_isStatic=false; m_isPulldown=false; m_isPullup=false;
|
||||
m_isIfaceParent=false;
|
||||
m_trace=false;
|
||||
}
|
||||
public:
|
||||
AstVar(FileLine* fl, AstVarType type, const string& name, VFlagChildDType, AstNodeDType* dtp)
|
||||
:AstNode(fl)
|
||||
, m_name(name) {
|
||||
, m_name(name), m_origName(name) {
|
||||
init();
|
||||
combineType(type);
|
||||
childDTypep(dtp); // Only for parser
|
||||
@@ -866,7 +911,7 @@ public:
|
||||
}
|
||||
AstVar(FileLine* fl, AstVarType type, const string& name, AstNodeDType* dtp)
|
||||
:AstNode(fl)
|
||||
, m_name(name) {
|
||||
, m_name(name), m_origName(name) {
|
||||
init();
|
||||
combineType(type);
|
||||
UASSERT(dtp,"AstVar created with no dtype");
|
||||
@@ -874,21 +919,21 @@ public:
|
||||
}
|
||||
AstVar(FileLine* fl, AstVarType type, const string& name, VFlagLogicPacked, int wantwidth)
|
||||
:AstNode(fl)
|
||||
, m_name(name) {
|
||||
, m_name(name), m_origName(name) {
|
||||
init();
|
||||
combineType(type);
|
||||
dtypeSetLogicSized(wantwidth,wantwidth,AstNumeric::UNSIGNED);
|
||||
}
|
||||
AstVar(FileLine* fl, AstVarType type, const string& name, VFlagBitPacked, int wantwidth)
|
||||
:AstNode(fl)
|
||||
, m_name(name) {
|
||||
, m_name(name), m_origName(name) {
|
||||
init();
|
||||
combineType(type);
|
||||
dtypeSetLogicSized(wantwidth,wantwidth,AstNumeric::UNSIGNED);
|
||||
}
|
||||
AstVar(FileLine* fl, AstVarType type, const string& name, AstVar* examplep)
|
||||
:AstNode(fl)
|
||||
, m_name(name) {
|
||||
, m_name(name), m_origName(name) {
|
||||
init();
|
||||
combineType(type);
|
||||
if (examplep->childDTypep()) {
|
||||
@@ -898,10 +943,13 @@ public:
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Var, VAR)
|
||||
virtual void dump(ostream& str);
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual bool hasDType() const { return true; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
AstVarType varType() const { return m_varType; } // * = Type of variable
|
||||
string origName() const { return m_origName; } // * = Original name
|
||||
void origName(const string& name) { m_origName = name; }
|
||||
AstVarType varType() const { return m_varType; } // * = Type of variable
|
||||
void varType(AstVarType type) { m_varType = type; }
|
||||
void varType2Out() { m_tristate=0; m_input=0; m_output=1; }
|
||||
void varType2In() { m_tristate=0; m_input=1; m_output=0; }
|
||||
string scType() const; // Return SysC type: bool, uint32_t, uint64_t, sc_bv
|
||||
@@ -940,6 +988,7 @@ public:
|
||||
void primaryIO(bool flag) { m_primaryIO = flag; }
|
||||
void isConst(bool flag) { m_isConst = flag; }
|
||||
void isStatic(bool flag) { m_isStatic = flag; }
|
||||
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
|
||||
void funcLocal(bool flag) { m_funcLocal = flag; }
|
||||
void funcReturn(bool flag) { m_funcReturn = flag; }
|
||||
void trace(bool flag) { m_trace=flag; }
|
||||
@@ -955,6 +1004,8 @@ public:
|
||||
bool isPrimaryIO() const { return m_primaryIO; }
|
||||
bool isPrimaryIn() const { return isPrimaryIO() && isInput(); }
|
||||
bool isIO() const { return (m_input||m_output); }
|
||||
bool isIfaceRef() const { return (varType()==AstVarType::IFACEREF); }
|
||||
bool isIfaceParent() const { return m_isIfaceParent; }
|
||||
bool isSignal() const { return varType().isSignal(); }
|
||||
bool isTemp() const { return (varType()==AstVarType::BLOCKTEMP || varType()==AstVarType::MODULETEMP
|
||||
|| varType()==AstVarType::STMTTEMP || varType()==AstVarType::XTEMP); }
|
||||
@@ -975,6 +1026,8 @@ public:
|
||||
bool isSc() const { return m_sc; }
|
||||
bool isScQuad() const;
|
||||
bool isScBv() const;
|
||||
bool isScUint() const;
|
||||
bool isScBigUint() const;
|
||||
bool isScSensitive() const { return m_scSensitive; }
|
||||
bool isSigPublic() const;
|
||||
bool isSigModPublic() const { return m_sigModPublic; }
|
||||
@@ -1073,7 +1126,7 @@ public:
|
||||
,m_name(name) ,m_aboveScopep(aboveScopep) ,m_aboveCellp(aboveCellp), m_modp(modp) {}
|
||||
ASTNODE_NODE_FUNCS(Scope, SCOPE)
|
||||
virtual void cloneRelink();
|
||||
virtual bool broken() const;
|
||||
virtual const char* broken() const;
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual string name() const { return m_name; } // * = Scope name
|
||||
virtual void name(const string& name) { m_name = name; }
|
||||
@@ -1127,8 +1180,8 @@ public:
|
||||
UASSERT(m_scopep->clonep(), "No clone cross link: "<<this);
|
||||
m_scopep = m_scopep->clonep()->castScope();
|
||||
}}
|
||||
virtual bool broken() const { return ( (m_varp && !m_varp->brokeExists())
|
||||
|| (m_scopep && !m_scopep->brokeExists())); }
|
||||
virtual const char* broken() const { BROKEN_RTN(m_varp && !m_varp->brokeExists());
|
||||
BROKEN_RTN(m_scopep && !m_scopep->brokeExists()); return NULL; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual string name() const {return scopep()->name()+"->"+varp()->name();} // * = Var name
|
||||
virtual void dump(ostream& str);
|
||||
@@ -1228,7 +1281,7 @@ public:
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Pin, PIN)
|
||||
virtual void dump(ostream& str);
|
||||
virtual bool broken() const { return (m_modVarp && !m_modVarp->brokeExists()); }
|
||||
virtual const char* broken() const { BROKEN_RTN(m_modVarp && !m_modVarp->brokeExists()); return NULL; }
|
||||
virtual string name() const { return m_name; } // * = Pin name, ""=go by number
|
||||
virtual void name(const string& name) { m_name = name; }
|
||||
bool dotStar() const { return name() == ".*"; } // Special fake name for .* connections until linked
|
||||
@@ -1241,6 +1294,25 @@ public:
|
||||
void svImplicit(bool flag) { m_svImplicit=flag; }
|
||||
};
|
||||
|
||||
struct AstArg : public AstNode {
|
||||
// An argument to a function/task
|
||||
private:
|
||||
string m_name; // Pin name, or "" for number based interconnect
|
||||
public:
|
||||
AstArg(FileLine* fl, const string& name, AstNode* exprp)
|
||||
: AstNode(fl)
|
||||
,m_name(name) {
|
||||
setNOp1p(exprp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Arg, ARG)
|
||||
virtual string name() const { return m_name; } // * = Pin name, ""=go by number
|
||||
virtual void name(const string& name) { m_name = name; }
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
void exprp(AstNode* nodep) { addOp1p(nodep); }
|
||||
AstNode* exprp() const { return op1p()->castNode(); } // op1 = Expression connected to pin, NULL if unconnected
|
||||
bool emptyConnectNoNext() const { return !exprp() && name()=="" && !nextp(); }
|
||||
};
|
||||
|
||||
struct AstModule : public AstNodeModule {
|
||||
// A module declaration
|
||||
AstModule(FileLine* fl, const string& name)
|
||||
@@ -1284,7 +1356,7 @@ public:
|
||||
AstPackageImport(FileLine* fl, AstPackage* packagep, const string& name)
|
||||
: AstNode (fl), m_name(name), m_packagep(packagep) {}
|
||||
ASTNODE_NODE_FUNCS(PackageImport, PACKAGEIMPORT)
|
||||
virtual bool broken() const { return (!m_packagep || !m_packagep->brokeExists()); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!m_packagep || !m_packagep->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_packagep && m_packagep->clonep()) m_packagep = m_packagep->clonep()->castPackage(); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual string name() const { return m_name; }
|
||||
@@ -1292,6 +1364,49 @@ public:
|
||||
void packagep(AstPackage* nodep) { m_packagep=nodep; }
|
||||
};
|
||||
|
||||
struct AstIface : public AstNodeModule {
|
||||
// A module declaration
|
||||
AstIface(FileLine* fl, const string& name)
|
||||
: AstNodeModule (fl,name) { }
|
||||
ASTNODE_NODE_FUNCS(Iface, IFACE)
|
||||
};
|
||||
|
||||
struct AstModportVarRef : public AstNode {
|
||||
// A input/output/etc variable referenced under a modport
|
||||
// The storage for the variable itself is inside the interface, thus this is a reference
|
||||
// PARENT: AstIface
|
||||
private:
|
||||
string m_name; // Name of the variable referenced
|
||||
AstVarType m_type; // Type of the variable (in/out)
|
||||
AstVar* m_varp; // Link to the actual Var
|
||||
public:
|
||||
AstModportVarRef(FileLine* fl, const string& name, AstVarType::en type)
|
||||
: AstNode(fl), m_name(name), m_type(type), m_varp(NULL) { }
|
||||
ASTNODE_NODE_FUNCS(ModportVarRef, MODPORTVARREF)
|
||||
virtual const char* broken() const { BROKEN_RTN(m_varp && !m_varp->brokeExists()); return NULL; }
|
||||
virtual void dump(ostream& str);
|
||||
AstVarType varType() const { return m_type; } // * = Type of variable
|
||||
virtual string name() const { return m_name; }
|
||||
bool isInput() const { return (varType()==AstVarType::INPUT || varType()==AstVarType::INOUT); }
|
||||
bool isOutput() const { return (varType()==AstVarType::OUTPUT || varType()==AstVarType::INOUT); }
|
||||
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
|
||||
void varp(AstVar* varp) { m_varp=varp; }
|
||||
};
|
||||
|
||||
struct AstModport : public AstNode {
|
||||
// A modport in an interface
|
||||
private:
|
||||
string m_name; // Name of the modport
|
||||
public:
|
||||
AstModport(FileLine* fl, const string& name, AstModportVarRef* varsp)
|
||||
: AstNode(fl), m_name(name) {
|
||||
addNOp1p(varsp); }
|
||||
virtual string name() const { return m_name; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
ASTNODE_NODE_FUNCS(Modport, MODPORT)
|
||||
AstModportVarRef* varsp() const { return op1p()->castModportVarRef(); } // op1 = List of Vars
|
||||
};
|
||||
|
||||
struct AstCell : public AstNode {
|
||||
// A instantiation cell or interface call (don't know which until link)
|
||||
private:
|
||||
@@ -1299,17 +1414,18 @@ private:
|
||||
string m_origName; // Original name before dot addition
|
||||
string m_modName; // Module the cell instances
|
||||
AstNodeModule* m_modp; // [AfterLink] Pointer to module instanced
|
||||
bool m_hasIfaceVar; // True if a Var has been created for this cell
|
||||
public:
|
||||
AstCell(FileLine* fl, const string& instName, const string& modName,
|
||||
AstPin* pinsp, AstPin* paramsp, AstRange* rangep)
|
||||
: AstNode(fl)
|
||||
, m_name(instName), m_origName(instName), m_modName(modName)
|
||||
, m_modp(NULL) {
|
||||
, m_modp(NULL), m_hasIfaceVar(false) {
|
||||
addNOp1p(pinsp); addNOp2p(paramsp); setNOp3p(rangep); }
|
||||
ASTNODE_NODE_FUNCS(Cell, CELL)
|
||||
// No cloneRelink, we presume cloneee's want the same module linkages
|
||||
virtual void dump(ostream& str);
|
||||
virtual bool broken() const { return (m_modp && !m_modp->brokeExists()); }
|
||||
virtual const char* broken() const { BROKEN_RTN(m_modp && !m_modp->brokeExists()); return NULL; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
// ACCESSORS
|
||||
virtual string name() const { return m_name; } // * = Cell name
|
||||
@@ -1325,6 +1441,8 @@ public:
|
||||
void addPinsp(AstPin* nodep) { addOp1p(nodep); }
|
||||
void addParamsp(AstPin* nodep) { addOp2p(nodep); }
|
||||
void modp(AstNodeModule* nodep) { m_modp = nodep; }
|
||||
bool hasIfaceVar() const { return m_hasIfaceVar; }
|
||||
void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; }
|
||||
};
|
||||
|
||||
struct AstCellInline : public AstNode {
|
||||
@@ -1434,7 +1552,7 @@ public:
|
||||
: AstNode(fl), m_packagep(packagep) {}
|
||||
ASTNODE_NODE_FUNCS(PackageRef, PACKAGEREF)
|
||||
// METHODS
|
||||
virtual bool broken() const { return !m_packagep || !m_packagep->brokeExists(); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!m_packagep || !m_packagep->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_packagep && m_packagep->clonep()) {
|
||||
m_packagep = m_packagep->clonep()->castPackage();
|
||||
}}
|
||||
@@ -1617,15 +1735,19 @@ public:
|
||||
};
|
||||
|
||||
struct AstAlways : public AstNode {
|
||||
AstAlways(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp)
|
||||
: AstNode(fl) {
|
||||
VAlwaysKwd m_keyword;
|
||||
public:
|
||||
AstAlways(FileLine* fl, VAlwaysKwd keyword, AstSenTree* sensesp, AstNode* bodysp)
|
||||
: AstNode(fl), m_keyword(keyword) {
|
||||
addNOp1p(sensesp); addNOp2p(bodysp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Always, ALWAYS)
|
||||
//
|
||||
virtual void dump(ostream& str);
|
||||
AstSenTree* sensesp() const { return op1p()->castSenTree(); } // op1 = Sensitivity list
|
||||
AstNode* bodysp() const { return op2p()->castNode(); } // op2 = Statements to evaluate
|
||||
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
|
||||
VAlwaysKwd keyword() const { return m_keyword; }
|
||||
// Special accessors
|
||||
bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); }
|
||||
};
|
||||
@@ -1696,13 +1818,23 @@ struct AstAssignW : public AstNodeAssign {
|
||||
AstAlways* convertToAlways() {
|
||||
AstNode* lhs1p = lhsp()->unlinkFrBack();
|
||||
AstNode* rhs1p = rhsp()->unlinkFrBack();
|
||||
AstAlways* newp = new AstAlways (fileline(), NULL,
|
||||
AstAlways* newp = new AstAlways (fileline(), VAlwaysKwd::ALWAYS, NULL,
|
||||
new AstAssign (fileline(), lhs1p, rhs1p));
|
||||
replaceWith(newp); // User expected to then deleteTree();
|
||||
return newp;
|
||||
}
|
||||
};
|
||||
|
||||
struct AstAssignVarScope : public AstNodeAssign {
|
||||
// Assign two VarScopes to each other
|
||||
AstAssignVarScope(FileLine* fileline, AstNode* lhsp, AstNode* rhsp)
|
||||
: AstNodeAssign(fileline, lhsp, rhsp) {
|
||||
dtypeFrom(rhsp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(AssignVarScope, ASSIGNVARSCOPE)
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssignVarScope(this->fileline(), lhsp, rhsp); }
|
||||
};
|
||||
|
||||
struct AstPull : public AstNode {
|
||||
private:
|
||||
bool m_direction;
|
||||
@@ -1789,10 +1921,10 @@ public:
|
||||
m_dataDeclp = NULL;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(CoverDecl, COVERDECL)
|
||||
virtual bool broken() const {
|
||||
if (m_dataDeclp && !m_dataDeclp->brokeExists()) return true;
|
||||
virtual const char* broken() const {
|
||||
BROKEN_RTN(m_dataDeclp && !m_dataDeclp->brokeExists());
|
||||
if (m_dataDeclp && m_dataDeclp->m_dataDeclp) v3fatalSrc("dataDeclp should point to real data, not be a list"); // Avoid O(n^2) accessing
|
||||
return false; }
|
||||
return NULL; }
|
||||
virtual void cloneRelink() { if (m_dataDeclp && m_dataDeclp->clonep()) m_dataDeclp = m_dataDeclp->clonep()->castCoverDecl(); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual int instrCount() const { return 1+2*instrCountLd(); }
|
||||
@@ -1830,7 +1962,7 @@ public:
|
||||
m_declp = declp;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(CoverInc, COVERINC)
|
||||
virtual bool broken() const { return !declp()->brokeExists(); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!declp()->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_declp->clonep()) m_declp = m_declp->clonep()->castCoverDecl(); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual int instrCount() const { return 1+2*instrCountLd(); }
|
||||
@@ -1981,7 +2113,7 @@ public:
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Display, DISPLAY)
|
||||
virtual void dump(ostream& str);
|
||||
virtual bool broken() const { return !fmtp(); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!fmtp()); return NULL; }
|
||||
virtual string verilogKwd() const { return (filep() ? (string)"$f"+(string)displayType().ascii()
|
||||
: (string)"$"+(string)displayType().ascii()); }
|
||||
virtual bool isGateOptimizable() const { return false; }
|
||||
@@ -2011,7 +2143,7 @@ struct AstSFormat : public AstNode {
|
||||
setOp3p(lhsp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(SFormat, SFORMAT)
|
||||
virtual bool broken() const { return !fmtp(); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!fmtp()); return NULL; }
|
||||
virtual string verilogKwd() const { return "$sformat"; }
|
||||
virtual string emitVerilog() { V3ERROR_NA; return ""; }
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
@@ -2432,7 +2564,7 @@ public:
|
||||
m_labelp = labelp;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(JumpGo, JUMPGO)
|
||||
virtual bool broken() const { return !labelp()->brokeExistsAbove(); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!labelp()->brokeExistsAbove()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_labelp->clonep()) m_labelp = m_labelp->clonep()->castJumpLabel(); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual int instrCount() const { return instrCountBranch(); }
|
||||
@@ -2709,7 +2841,7 @@ public:
|
||||
addNOp2p(valuep);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(TraceInc, TRACEINC)
|
||||
virtual bool broken() const { return !declp()->brokeExists(); }
|
||||
virtual const char* broken() const { BROKEN_RTN(!declp()->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() { if (m_declp->clonep()) m_declp = m_declp->clonep()->castTraceDecl(); }
|
||||
virtual void dump(ostream& str);
|
||||
virtual int instrCount() const { return 10+2*instrCountLd(); }
|
||||
@@ -2746,7 +2878,7 @@ public:
|
||||
ASTNODE_NODE_FUNCS(Active, ACTIVE)
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual string name() const { return m_name; }
|
||||
virtual bool broken() const { return (m_sensesp && !m_sensesp->brokeExists()); }
|
||||
virtual const char* broken() const { BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists()); return NULL; }
|
||||
virtual void cloneRelink() {
|
||||
if (m_sensesp->clonep()) {
|
||||
m_sensesp = m_sensesp->clonep()->castSenTree();
|
||||
@@ -3180,6 +3312,20 @@ struct AstCast : public AstNode {
|
||||
AstNodeDType* childDTypep() const { return op2p()->castNodeDType(); }
|
||||
};
|
||||
|
||||
struct AstCastSize : public AstNode {
|
||||
// Cast to specific size; signed/twostate inherited from lower element per IEEE
|
||||
AstCastSize(FileLine* fl, AstNode* lhsp, AstConst* rhsp) : AstNode(fl) {
|
||||
setOp1p(lhsp); setOp2p(rhsp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(CastSize, CASTSIZE)
|
||||
virtual string emitVerilog() { return "((%r)'(%l))"; }
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
virtual bool cleanOut() { V3ERROR_NA; return true;} virtual bool cleanLhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return false;}
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
};
|
||||
|
||||
struct AstCCast : public AstNodeUniop {
|
||||
// Cast to C-based data type
|
||||
private:
|
||||
@@ -3232,6 +3378,7 @@ struct AstFEof : public AstNodeUniop {
|
||||
virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*16; }
|
||||
virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
AstNode* filep() const { return lhsp(); }
|
||||
};
|
||||
|
||||
@@ -3245,6 +3392,7 @@ struct AstFGetC : public AstNodeUniop {
|
||||
virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*64; }
|
||||
virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
AstNode* filep() const { return lhsp(); }
|
||||
};
|
||||
|
||||
@@ -3999,7 +4147,7 @@ struct AstPattern : public AstNodeMath {
|
||||
// Parents: AstNodeAssign, AstPattern, ...
|
||||
// Children: expression, AstPattern, AstPatReplicate
|
||||
AstPattern(FileLine* fl, AstNode* itemsp) : AstNodeMath(fl) {
|
||||
addNOp1p(itemsp);
|
||||
addNOp2p(itemsp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Pattern, PATTERN)
|
||||
virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially
|
||||
@@ -4008,7 +4156,11 @@ struct AstPattern : public AstNodeMath {
|
||||
virtual string emitSimpleOperator() { V3ERROR_NA; return "";}
|
||||
virtual bool cleanOut() {V3ERROR_NA; return "";}
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
AstNode* itemsp() const { return op1p(); } // op1 = AstPatReplicate, AstPatMember, etc
|
||||
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return op1p()->castNodeDType(); } // op1 = Type assigning to
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
AstNode* itemsp() const { return op2p(); } // op2 = AstPatReplicate, AstPatMember, etc
|
||||
};
|
||||
struct AstPatMember : public AstNodeMath {
|
||||
// Verilog '{a} or '{a{b}}
|
||||
@@ -4330,7 +4482,7 @@ public:
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(CFunc, CFUNC)
|
||||
virtual string name() const { return m_name; }
|
||||
virtual bool broken() const { return ( (m_scopep && !m_scopep->brokeExists())); }
|
||||
virtual const char* broken() const { BROKEN_RTN((m_scopep && !m_scopep->brokeExists())); return NULL; }
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
virtual void dump(ostream& str=cout);
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
@@ -4418,7 +4570,7 @@ public:
|
||||
virtual void cloneRelink() { if (m_funcp && m_funcp->clonep()) {
|
||||
m_funcp = m_funcp->clonep()->castCFunc();
|
||||
}}
|
||||
virtual bool broken() const { return (m_funcp && !m_funcp->brokeExists()); }
|
||||
virtual const char* broken() const { BROKEN_RTN(m_funcp && !m_funcp->brokeExists()); return NULL; }
|
||||
virtual int instrCount() const { return instrCountCall(); }
|
||||
virtual V3Hash sameHash() const { return V3Hash(funcp()); }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
@@ -4550,7 +4702,7 @@ public:
|
||||
m_dollarUnitPkgp = NULL;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Netlist, NETLIST)
|
||||
virtual bool broken() const { return (m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists()); }
|
||||
virtual const char* broken() const { BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists()); return NULL; }
|
||||
AstNodeModule* modulesp() const { return op1p()->castNodeModule();} // op1 = List of modules
|
||||
AstNodeModule* topModulep() const { return op1p()->castNodeModule(); } // * = Top module in hierarchy (first one added, for now)
|
||||
void addModulep(AstNodeModule* modulep) { addOp1p(modulep); }
|
||||
|
||||
+2
-2
@@ -202,8 +202,8 @@ class BrokenCheckVisitor : public AstNVisitor {
|
||||
private:
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
BrokenTable::setUnder(nodep,true);
|
||||
if (nodep->broken()) {
|
||||
nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo)");
|
||||
if (const char* whyp=nodep->broken()) {
|
||||
nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo): "<<whyp);
|
||||
}
|
||||
if (nodep->dtypep()) {
|
||||
if (!nodep->dtypep()->brokeExists()) { nodep->v3fatalSrc("Broken link in node->dtypep() to "<<(void*)nodep->dtypep()); }
|
||||
|
||||
+1
-1
@@ -526,7 +526,7 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
sort(report.begin(), report.end());
|
||||
stable_sort(report.begin(), report.end());
|
||||
for (deque<string>::iterator it = report.begin(); it!=report.end(); ++it) {
|
||||
*ofp << *it;
|
||||
}
|
||||
|
||||
+7
-6
@@ -87,15 +87,16 @@ private:
|
||||
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;
|
||||
int msb = isArray ? arrayp->msb() : 0;
|
||||
int lsb = isArray ? arrayp->lsb() : 0;
|
||||
if (isArray && ((msb - lsb + 1) > DETECTARRAY_MAX_INDEXES)) {
|
||||
bool isStruct = structp && structp->packed();
|
||||
int elements = isArray ? arrayp->elementsConst() : 1;
|
||||
if (isArray && (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(msb-lsb+1));
|
||||
} else if (!isArray
|
||||
<<"... Could recompile with DETECTARRAY_MAX_INDEXES increased to at least "<<cvtToStr(elements));
|
||||
} else if (!isArray && !isStruct
|
||||
&& !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());
|
||||
@@ -109,7 +110,7 @@ private:
|
||||
m_topModp->addStmtp(newvarp);
|
||||
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopetopp, newvarp);
|
||||
m_scopetopp->addVarp(newvscp);
|
||||
for (int index=lsb; index<=msb; ++index) {
|
||||
for (int index=0; index<elements; ++index) {
|
||||
AstChangeDet* changep
|
||||
= new AstChangeDet (vscp->fileline(),
|
||||
aselIfNeeded(isArray, index,
|
||||
|
||||
@@ -684,6 +684,7 @@ class GaterVisitor : public GaterBaseVisitor {
|
||||
AstNode* bodyp = nodep->bodysp()->cloneTree(true);
|
||||
|
||||
AstAlways* alwp = new AstAlways(nodep->fileline(),
|
||||
nodep->keyword(),
|
||||
sensesp,
|
||||
bodyp);
|
||||
|
||||
|
||||
+4
-8
@@ -346,14 +346,10 @@ private:
|
||||
nodep->iterateChildren(*this);
|
||||
// Link to global function
|
||||
if (nodep->formCallTree()) {
|
||||
if (nodep->name() == "_final") {
|
||||
UINFO(4, " formCallTree "<<nodep<<endl);
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
|
||||
callp->argTypes("vlSymsp");
|
||||
m_finalFuncp->addStmtsp(callp);
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown CFunc name. Make code more generic, with a map of func names");
|
||||
}
|
||||
UINFO(4, " formCallTree "<<nodep<<endl);
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
|
||||
callp->argTypes("vlSymsp");
|
||||
m_finalFuncp->addStmtsp(callp);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstSenTree* nodep, AstNUser*) {
|
||||
|
||||
+12
-3
@@ -815,9 +815,10 @@ private:
|
||||
|
||||
bool varNotReferenced(AstNode* nodep, AstVar* varp, int level=0) {
|
||||
// Return true if varp never referenced under node.
|
||||
// Return false if referenced, or tree too deep to be worth it
|
||||
// Return false if referenced, or tree too deep to be worth it, or side effects
|
||||
if (!nodep) return true;
|
||||
if (level>2) return false;
|
||||
if (nodep->isPure()) return false; // For example a $fgetc can't be reordered
|
||||
if (nodep->castNodeVarRef() && nodep->castNodeVarRef()->varp()==varp) return false;
|
||||
return (varNotReferenced (nodep->nextp(),varp,level+1)
|
||||
&& varNotReferenced(nodep->op1p(),varp,level+1)
|
||||
@@ -1230,6 +1231,8 @@ private:
|
||||
&& ((!m_params // Can reduce constant wires into equations
|
||||
&& m_doNConst
|
||||
&& v3Global.opt.oConst()
|
||||
&& !(nodep->varp()->isFuncLocal() // Default value, not a "known" constant for this usage
|
||||
&& nodep->varp()->isInput())
|
||||
&& !nodep->varp()->isSigPublic())
|
||||
|| nodep->varp()->isParam())) {
|
||||
AstConst* constp = nodep->varp()->valuep()->castConst();
|
||||
@@ -1413,7 +1416,7 @@ private:
|
||||
for (AstNodeSenItem* senp = nodep->sensesp()->castNodeSenItem(); senp; senp=senp->nextp()->castNodeSenItem()) {
|
||||
vec.push_back(senp);
|
||||
}
|
||||
sort(vec.begin(), vec.end(), SenItemCmp());
|
||||
stable_sort(vec.begin(), vec.end(), SenItemCmp());
|
||||
for (vector<AstNodeSenItem*>::iterator it=vec.begin(); it!=vec.end(); ++it) {
|
||||
(*it)->unlinkFrBack();
|
||||
}
|
||||
@@ -1466,6 +1469,9 @@ private:
|
||||
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
|
||||
// Don't perform any optimizations, keep the alias around
|
||||
}
|
||||
virtual void visit(AstAssignVarScope* nodep, AstNUser*) {
|
||||
// Don't perform any optimizations, the node won't be linked yet
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
if (m_doNConst && replaceNodeAssign(nodep)) return;
|
||||
@@ -1640,7 +1646,10 @@ private:
|
||||
replaceWithSimulation(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstArg* nodep, AstNUser*) {
|
||||
// replaceWithSimulation on the Arg's parent FuncRef replaces these
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstWhile* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
if (m_doNConst) {
|
||||
|
||||
+8
-4
@@ -64,6 +64,7 @@ private:
|
||||
bool m_checkBlock; // Should this block get covered?
|
||||
AstNodeModule* m_modp; // Current module to add statement to
|
||||
bool m_inToggleOff; // In function/task etc
|
||||
bool m_inModOff; // In module with no coverage
|
||||
FileMap m_fileps; // Column counts for each fileline
|
||||
string m_beginHier; // AstBegin hier name for user coverage points
|
||||
|
||||
@@ -124,9 +125,11 @@ private:
|
||||
// VISITORS - BOTH
|
||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||
m_modp = nodep;
|
||||
m_inModOff = nodep->isTop(); // Ignore coverage on top module; it's a shell we created
|
||||
m_fileps.clear();
|
||||
nodep->iterateChildren(*this);
|
||||
m_modp = NULL;
|
||||
m_inModOff = true;
|
||||
}
|
||||
|
||||
// VISITORS - TOGGLE COVERAGE
|
||||
@@ -140,7 +143,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
if (m_modp && !m_inToggleOff
|
||||
if (m_modp && !m_inModOff && !m_inToggleOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageToggle()) {
|
||||
const char* disablep = varIgnoreToggle(nodep);
|
||||
if (disablep) {
|
||||
@@ -231,7 +234,7 @@ private:
|
||||
UINFO(4," IF: "<<nodep<<endl);
|
||||
if (m_checkBlock) {
|
||||
nodep->ifsp()->iterateAndNext(*this);
|
||||
if (m_checkBlock
|
||||
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
|
||||
@@ -243,7 +246,7 @@ private:
|
||||
if (nodep->elsesp()) {
|
||||
m_checkBlock = true;
|
||||
nodep->elsesp()->iterateAndNext(*this);
|
||||
if (m_checkBlock
|
||||
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()) {
|
||||
@@ -258,7 +261,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstCaseItem* nodep, AstNUser*) {
|
||||
UINFO(4," CASEI: "<<nodep<<endl);
|
||||
if (m_checkBlock
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) {
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
if (m_checkBlock) { // if the case body didn't disable it
|
||||
@@ -327,6 +330,7 @@ public:
|
||||
m_checkBlock = true;
|
||||
m_beginHier = "";
|
||||
m_inToggleOff = false;
|
||||
m_inModOff = true;
|
||||
rootp->iterateChildren(*this);
|
||||
}
|
||||
virtual ~CoverageVisitor() {}
|
||||
|
||||
+3
-3
@@ -124,10 +124,10 @@ private:
|
||||
AstVar* varp;
|
||||
AstNodeModule* addmodp = oldvarscp->scopep()->modp();
|
||||
// We need a new AstVar, but only one for all scopes, to match the new AstVarScope
|
||||
VarMap::iterator iter = m_modVarMap.find(make_pair(addmodp,name));
|
||||
if (iter != m_modVarMap.end()) {
|
||||
VarMap::iterator it = m_modVarMap.find(make_pair(addmodp,name));
|
||||
if (it != m_modVarMap.end()) {
|
||||
// Created module's AstVar earlier under some other scope
|
||||
varp = iter->second;
|
||||
varp = it->second;
|
||||
} else {
|
||||
if (width==0) {
|
||||
varp = new AstVar (oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, oldvarscp->varp());
|
||||
|
||||
+61
-47
@@ -58,8 +58,9 @@ public:
|
||||
int splitFilenum() const { return m_splitFilenum; }
|
||||
int splitFilenumInc() { m_splitSize = 0; return ++m_splitFilenum; }
|
||||
int splitSize() const { return m_splitSize; }
|
||||
void splitSizeInc(AstNode* nodep) { m_splitSize += EmitCBaseCounterVisitor(nodep).count(); }
|
||||
bool splitNeeded() { return (splitSize() && v3Global.opt.outputSplit() > 1
|
||||
void splitSizeInc(int count) { m_splitSize += count; }
|
||||
void splitSizeInc(AstNode* nodep) { splitSizeInc(EmitCBaseCounterVisitor(nodep).count()); }
|
||||
bool splitNeeded() { return (splitSize() && v3Global.opt.outputSplit()
|
||||
&& v3Global.opt.outputSplit() < splitSize()); }
|
||||
|
||||
// METHODS
|
||||
@@ -79,10 +80,20 @@ public:
|
||||
puts (nodep->isWide()?"W":(nodep->isQuad()?"Q":"I"));
|
||||
}
|
||||
void emitScIQW(AstVar* nodep) {
|
||||
puts (nodep->isScBv()?"SW":(nodep->isScQuad()?"SQ":"SI"));
|
||||
puts (nodep->isScBigUint() ? "SB"
|
||||
: nodep->isScUint() ? "SU"
|
||||
: nodep->isScBv() ? "SW"
|
||||
: (nodep->isScQuad() ? "SQ" : "SI"));
|
||||
}
|
||||
void emitOpName(AstNode* nodep, const string& format,
|
||||
AstNode* lhsp, AstNode* rhsp, AstNode* thsp);
|
||||
void emitDeclArrayBrackets(AstVar* nodep) {
|
||||
// This isn't very robust and may need cleanup for other data types
|
||||
for (AstUnpackArrayDType* arrayp=nodep->dtypeSkipRefp()->castUnpackArrayDType(); arrayp;
|
||||
arrayp = arrayp->subDTypep()->skipRefp()->castUnpackArrayDType()) {
|
||||
puts("["+cvtToStr(arrayp->elementsConst())+"]");
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
@@ -785,7 +796,7 @@ class EmitCImp : EmitCStmts {
|
||||
|
||||
if (!m_blkChangeDetVec.empty()) puts("return __req;\n");
|
||||
|
||||
// puts("__Vm_activity = true;\n");
|
||||
//puts("__Vm_activity = true;\n");
|
||||
puts("}\n");
|
||||
}
|
||||
|
||||
@@ -872,7 +883,6 @@ public:
|
||||
void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
|
||||
AstBasicDType* basicp = nodep->basicp(); if (!basicp) nodep->v3fatalSrc("Unimplemented: Outputting this data type");
|
||||
if (nodep->isIO()) {
|
||||
bool isArray = !nodep->dtypeSkipRefp()->castBasicDType();
|
||||
if (nodep->isSc()) {
|
||||
m_ctorVarsVec.push_back(nodep);
|
||||
ofp()->putAlign(nodep->isStatic(), 4); // sc stuff is a structure, so bigger alignment
|
||||
@@ -888,12 +898,7 @@ void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
|
||||
puts(">\t");
|
||||
}
|
||||
puts(nodep->name());
|
||||
if (isArray) {
|
||||
for (AstUnpackArrayDType* arrayp=nodep->dtypeSkipRefp()->castUnpackArrayDType(); arrayp;
|
||||
arrayp = arrayp->subDTypep()->skipRefp()->castUnpackArrayDType()) {
|
||||
puts("["+cvtToStr(arrayp->elementsConst())+"]");
|
||||
}
|
||||
}
|
||||
emitDeclArrayBrackets(nodep);
|
||||
puts(";\n");
|
||||
} else { // C++ signals
|
||||
ofp()->putAlign(nodep->isStatic(), nodep->dtypeSkipRefp()->widthAlignBytes(),
|
||||
@@ -906,36 +911,20 @@ void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
|
||||
if (nodep->isQuad()) puts("64");
|
||||
else if (nodep->widthMin() <= 8) puts("8");
|
||||
else if (nodep->widthMin() <= 16) puts("16");
|
||||
else if (nodep->isWide()) puts("W");
|
||||
|
||||
if (isArray) {
|
||||
if (nodep->isWide()) puts("W");
|
||||
puts("("+nodep->name());
|
||||
for (AstUnpackArrayDType* arrayp=nodep->dtypeSkipRefp()->castUnpackArrayDType(); arrayp;
|
||||
arrayp = arrayp->subDTypep()->skipRefp()->castUnpackArrayDType()) {
|
||||
puts("["+cvtToStr(arrayp->elementsConst())+"]");
|
||||
}
|
||||
puts(","+cvtToStr(basicp->msb())+","+cvtToStr(basicp->lsb()));
|
||||
if (basicp->isWide()) puts(","+cvtToStr(basicp->widthWords()));
|
||||
} else {
|
||||
if (!basicp->isWide())
|
||||
puts("("+nodep->name()
|
||||
+","+cvtToStr(basicp->msb())
|
||||
+","+cvtToStr(basicp->lsb()));
|
||||
else puts("W("+nodep->name()
|
||||
+","+cvtToStr(basicp->msb())
|
||||
+","+cvtToStr(basicp->lsb())
|
||||
+","+cvtToStr(basicp->widthWords()));
|
||||
}
|
||||
puts("("+nodep->name());
|
||||
emitDeclArrayBrackets(nodep);
|
||||
// If it's a packed struct/array then nodep->width is the whole thing, msb/lsb is just lowest dimension
|
||||
puts(","+cvtToStr(basicp->lsb()+nodep->width()-1)
|
||||
+","+cvtToStr(basicp->lsb()));
|
||||
if (nodep->isWide()) puts(","+cvtToStr(nodep->widthWords()));
|
||||
puts(");\n");
|
||||
}
|
||||
} else if (basicp && basicp->isOpaque()) {
|
||||
// strings and other fundamental c types
|
||||
puts(nodep->vlArgType(true,false));
|
||||
// This isn't very robust and may need cleanup for other data types
|
||||
for (AstUnpackArrayDType* arrayp=nodep->dtypeSkipRefp()->castUnpackArrayDType(); arrayp;
|
||||
arrayp = arrayp->subDTypep()->skipRefp()->castUnpackArrayDType()) {
|
||||
puts("["+cvtToStr(arrayp->elementsConst())+"]");
|
||||
}
|
||||
emitDeclArrayBrackets(nodep);
|
||||
puts(";\n");
|
||||
} else {
|
||||
// Arrays need a small alignment, but may need different padding after.
|
||||
@@ -957,13 +946,10 @@ void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
|
||||
}
|
||||
if (prefixIfImp!="") { puts(prefixIfImp); puts("::"); }
|
||||
puts(nodep->name());
|
||||
// This isn't very robust and may need cleanup for other data types
|
||||
for (AstUnpackArrayDType* arrayp=nodep->dtypeSkipRefp()->castUnpackArrayDType(); arrayp;
|
||||
arrayp = arrayp->subDTypep()->skipRefp()->castUnpackArrayDType()) {
|
||||
puts("["+cvtToStr(arrayp->elementsConst())+"]");
|
||||
}
|
||||
emitDeclArrayBrackets(nodep);
|
||||
// If it's a packed struct/array then nodep->width is the whole thing, msb/lsb is just lowest dimension
|
||||
puts(","+cvtToStr(basicp->lsb()+nodep->width())+","+cvtToStr(basicp->lsb()));
|
||||
puts(","+cvtToStr(basicp->lsb()+nodep->width()-1)
|
||||
+","+cvtToStr(basicp->lsb()));
|
||||
if (nodep->isWide()) puts(","+cvtToStr(nodep->widthWords()));
|
||||
puts(");\n");
|
||||
}
|
||||
@@ -1433,9 +1419,11 @@ void EmitCImp::emitConfigureImp(AstNodeModule* modp) {
|
||||
puts("// Coverage Declarations\n");
|
||||
}
|
||||
nodep->accept(*this);
|
||||
splitSizeInc(nodep);
|
||||
}
|
||||
}
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
}
|
||||
|
||||
void EmitCImp::emitCoverageImp(AstNodeModule* modp) {
|
||||
@@ -1459,6 +1447,7 @@ void EmitCImp::emitCoverageImp(AstNodeModule* modp) {
|
||||
puts( " \"page\",pagep,");
|
||||
puts( " \"comment\",commentp);\n");
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1468,6 +1457,7 @@ void EmitCImp::emitDestructorImp(AstNodeModule* modp) {
|
||||
emitTextSection(AstType::atSCDTOR);
|
||||
if (modp->isTop()) puts("delete __VlSymsp; __VlSymsp=NULL;\n");
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
}
|
||||
|
||||
void EmitCImp::emitSavableImp(AstNodeModule* modp) {
|
||||
@@ -1646,6 +1636,7 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) {
|
||||
puts("}\n");
|
||||
#endif
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
//
|
||||
puts("\nvoid "+modClassName(modp)+"::_eval_initial_loop("+EmitCBaseVisitor::symClassVar()+") {\n");
|
||||
@@ -1666,6 +1657,7 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) {
|
||||
puts( "}\n");
|
||||
#endif
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -1699,7 +1691,7 @@ void EmitCStmts::emitVarList(AstNode* firstp, EisWhich which, const string& pref
|
||||
if (varp->isUsedClock() && varp->widthMin()==1) sortbytes = 0;
|
||||
else if (varp->dtypeSkipRefp()->castUnpackArrayDType()) sortbytes=8;
|
||||
else if (varp->basicp() && varp->basicp()->isOpaque()) sortbytes=7;
|
||||
else if (varp->isScBv()) sortbytes=6;
|
||||
else if (varp->isScBv() || varp->isScBigUint()) sortbytes=6;
|
||||
else if (sigbytes==8) sortbytes=5;
|
||||
else if (sigbytes==4) sortbytes=4;
|
||||
else if (sigbytes==2) sortbytes=2;
|
||||
@@ -1732,7 +1724,7 @@ void EmitCImp::emitIntFuncDecls(AstNodeModule* modp) {
|
||||
}
|
||||
}
|
||||
|
||||
sort(funcsp.begin(), funcsp.end(), CmpName());
|
||||
stable_sort(funcsp.begin(), funcsp.end(), CmpName());
|
||||
|
||||
for (vector<AstCFunc*>::iterator it = funcsp.begin(); it != funcsp.end(); ++it) {
|
||||
AstCFunc* funcp = *it;
|
||||
@@ -2126,6 +2118,7 @@ class EmitCTrace : EmitCStmts {
|
||||
+", &"+topClassName()+"::traceFull"
|
||||
+", &"+topClassName()+"::traceChg, this);\n");
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
puts("void "+topClassName()+"::traceInit("
|
||||
+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code) {\n");
|
||||
@@ -2138,6 +2131,7 @@ class EmitCTrace : EmitCStmts {
|
||||
puts("t->traceInitThis (vlSymsp, vcdp, code);\n");
|
||||
puts("vcdp->scopeEscape('.');\n"); // Restore so SystemPerl traced files won't break
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
puts("void "+topClassName()+"::traceFull("
|
||||
+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code) {\n");
|
||||
@@ -2146,6 +2140,7 @@ class EmitCTrace : EmitCStmts {
|
||||
puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n");
|
||||
puts("t->traceFullThis (vlSymsp, vcdp, code);\n");
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
puts("\n//======================\n\n");
|
||||
}
|
||||
@@ -2162,6 +2157,7 @@ class EmitCTrace : EmitCStmts {
|
||||
puts("t->traceChgThis (vlSymsp, vcdp, code);\n");
|
||||
puts("}\n");
|
||||
puts("}\n");
|
||||
splitSizeInc(10);
|
||||
|
||||
puts("\n//======================\n\n");
|
||||
}
|
||||
@@ -2172,6 +2168,21 @@ class EmitCTrace : EmitCStmts {
|
||||
AstVar* varp = varrefp->varp();
|
||||
return varp->isSc() && varp->isScBv();
|
||||
}
|
||||
|
||||
bool emitTraceIsScBigUint(AstTraceInc* nodep) {
|
||||
AstVarRef* varrefp = nodep->valuep()->castVarRef();
|
||||
if (!varrefp) return false;
|
||||
AstVar* varp = varrefp->varp();
|
||||
return varp->isSc() && varp->isScBigUint();
|
||||
}
|
||||
|
||||
bool emitTraceIsScUint(AstTraceInc* nodep) {
|
||||
AstVarRef* varrefp = nodep->valuep()->castVarRef();
|
||||
if (!varrefp) return false;
|
||||
AstVar* varp = varrefp->varp();
|
||||
return varp->isSc() && varp->isScUint();
|
||||
}
|
||||
|
||||
void emitTraceInitOne(AstTraceDecl* nodep) {
|
||||
if (nodep->isDouble()) {
|
||||
puts("vcdp->declDouble");
|
||||
@@ -2207,7 +2218,7 @@ class EmitCTrace : EmitCStmts {
|
||||
? "full":"chg");
|
||||
if (nodep->isDouble()) {
|
||||
puts("vcdp->"+full+"Double");
|
||||
} else if (nodep->isWide() || emitTraceIsScBv(nodep)) {
|
||||
} else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) {
|
||||
puts("vcdp->"+full+"Array");
|
||||
} else if (nodep->isQuad()) {
|
||||
puts("vcdp->"+full+"Quad ");
|
||||
@@ -2221,7 +2232,7 @@ class EmitCTrace : EmitCStmts {
|
||||
puts(",");
|
||||
emitTraceValue(nodep, arrayindex);
|
||||
if (!nodep->isDouble() // When float/double no longer have widths this can go
|
||||
&& (nodep->declp()->left() || nodep->declp()->right() || emitTraceIsScBv(nodep))) {
|
||||
&& (nodep->declp()->left() || nodep->declp()->right() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep))) {
|
||||
puts(","+cvtToStr(nodep->declp()->widthMin()));
|
||||
}
|
||||
puts(");\n");
|
||||
@@ -2231,7 +2242,8 @@ class EmitCTrace : EmitCStmts {
|
||||
AstVarRef* varrefp = nodep->valuep()->castVarRef();
|
||||
AstVar* varp = varrefp->varp();
|
||||
puts("(");
|
||||
if (emitTraceIsScBv(nodep)) puts("VL_SC_BV_DATAP(");
|
||||
if (emitTraceIsScBigUint(nodep)) puts("(vluint32_t*)");
|
||||
else if (emitTraceIsScBv(nodep)) puts("VL_SC_BV_DATAP(");
|
||||
varrefp->iterate(*this); // Put var name out
|
||||
// Tracing only supports 1D arrays
|
||||
if (varp->dtypeSkipRefp()->castUnpackArrayDType()) {
|
||||
@@ -2240,7 +2252,9 @@ class EmitCTrace : EmitCStmts {
|
||||
else puts("["+cvtToStr(arrayindex)+"]");
|
||||
}
|
||||
if (varp->isSc()) puts(".read()");
|
||||
if (emitTraceIsScBv(nodep)) puts(")");
|
||||
if (emitTraceIsScUint(nodep)) puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()");
|
||||
else if (emitTraceIsScBigUint(nodep)) puts(".get_raw()");
|
||||
else if (emitTraceIsScBv(nodep)) puts(")");
|
||||
puts(")");
|
||||
} else {
|
||||
puts("(");
|
||||
|
||||
+3
-3
@@ -102,7 +102,7 @@ class EmitCSyms : EmitCBaseVisitor {
|
||||
if (rsvd != "") {
|
||||
// Generally V3Name should find all of these and throw SYMRSVDWORD.
|
||||
// We'll still check here because the compiler errors resulting if we miss this warning are SO nasty
|
||||
nodep->v3error("Symbol matching "+rsvd+" reserved word reached emitter, should have hit SYMRSVDWORD: '"<<nodep->name()<<"'");
|
||||
nodep->v3error("Symbol matching "+rsvd+" reserved word reached emitter, should have hit SYMRSVDWORD: '"<<nodep->prettyName()<<"'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,8 +168,8 @@ class EmitCSyms : EmitCBaseVisitor {
|
||||
varsExpand();
|
||||
|
||||
// Sort by names, so line/process order matters less
|
||||
sort(m_scopes.begin(), m_scopes.end(), CmpName());
|
||||
sort(m_dpis.begin(), m_dpis.end(), CmpDpi());
|
||||
stable_sort(m_scopes.begin(), m_scopes.end(), CmpName());
|
||||
stable_sort(m_dpis.begin(), m_dpis.end(), CmpDpi());
|
||||
|
||||
// Output
|
||||
emitSymHdr();
|
||||
|
||||
@@ -525,6 +525,9 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
|
||||
nodep->pinsp()->iterateAndNext(*this);
|
||||
puts(")");
|
||||
}
|
||||
virtual void visit(AstArg* nodep, AstNUser*) {
|
||||
nodep->exprp()->iterateAndNext(*this);
|
||||
}
|
||||
// Terminals
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
if (nodep->varScopep())
|
||||
|
||||
+10
-3
@@ -44,6 +44,7 @@ bool V3Error::s_describedEachWarn[V3ErrorCode::_ENUM_MAX];
|
||||
bool V3Error::s_describedWarnings = false;
|
||||
bool V3Error::s_pretendError[V3ErrorCode::_ENUM_MAX];
|
||||
V3Error::MessagesSet V3Error::s_messages;
|
||||
V3Error::ErrorExitCb V3Error::s_errorExitCb = NULL;
|
||||
|
||||
struct v3errorIniter {
|
||||
v3errorIniter() { V3Error::init(); }
|
||||
@@ -89,8 +90,8 @@ const string FileLineSingleton::filenameLetters(int no) {
|
||||
//! We associate a language with each source file, so we also set the default
|
||||
//! for this.
|
||||
int FileLineSingleton::nameToNumber(const string& filename) {
|
||||
FileNameNumMap::const_iterator iter = m_namemap.find(filename);
|
||||
if (VL_LIKELY(iter != m_namemap.end())) return iter->second;
|
||||
FileNameNumMap::const_iterator it = m_namemap.find(filename);
|
||||
if (VL_LIKELY(it != m_namemap.end())) return it->second;
|
||||
int num = m_names.size();
|
||||
m_names.push_back(filename);
|
||||
m_languages.push_back(V3LangCode::mostRecent());
|
||||
@@ -482,7 +483,8 @@ void V3Error::v3errorEnd (ostringstream& sstr) {
|
||||
}
|
||||
#ifndef _V3ERROR_NO_GLOBAL_
|
||||
if (debug()) {
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree",99));
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree",990));
|
||||
if (s_errorExitCb) s_errorExitCb();
|
||||
V3Stats::statsFinalAll(v3Global.rootp());
|
||||
V3Stats::statsReport();
|
||||
}
|
||||
@@ -491,5 +493,10 @@ void V3Error::v3errorEnd (ostringstream& sstr) {
|
||||
|
||||
vlAbort();
|
||||
}
|
||||
else if (isError(s_errorCode, s_errorSuppressed)) {
|
||||
// We don't dump tree on any error because a Visitor may be in middle of
|
||||
// a tree cleanup and cause a false broken problem.
|
||||
if (s_errorExitCb) s_errorExitCb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -57,6 +57,7 @@ public:
|
||||
// Warning codes:
|
||||
EC_FIRST_WARN, // Just a code so the program knows where to start warnings
|
||||
//
|
||||
ALWCOMBORDER, // Always_comb with unordered statements
|
||||
ASSIGNDLY, // Assignment delays
|
||||
ASSIGNIN, // Assigning to input
|
||||
BLKANDNBLK, // Blocked and non-blocking assignments to same variable
|
||||
@@ -116,7 +117,7 @@ public:
|
||||
"BLKLOOPINIT", "DETECTARRAY", "MULTITOP", "TASKNSVAR",
|
||||
// Warnings
|
||||
" EC_FIRST_WARN",
|
||||
"ASSIGNDLY", "ASSIGNIN",
|
||||
"ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN",
|
||||
"BLKANDNBLK", "BLKSEQ",
|
||||
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CDCRSTLOGIC", "CMPCONST",
|
||||
"COMBDLY", "DEFPARAM", "DECLFILENAME",
|
||||
@@ -146,7 +147,8 @@ public:
|
||||
bool mentionManual() const { return ( m_e==EC_FATALSRC || pretendError() ); }
|
||||
|
||||
// Warnings that are lint only
|
||||
bool lintError() const { return ( m_e==CASEINCOMPLETE || m_e==CASEOVERLAP
|
||||
bool lintError() const { return ( m_e==ALWCOMBORDER
|
||||
|| m_e==CASEINCOMPLETE || m_e==CASEOVERLAP
|
||||
|| m_e==CASEWITHX || m_e==CASEX
|
||||
|| m_e==CMPCONST
|
||||
|| m_e==ENDLABEL
|
||||
@@ -179,6 +181,7 @@ class V3Error {
|
||||
// Base class for any object that wants debugging and error reporting
|
||||
|
||||
typedef set<string> MessagesSet;
|
||||
typedef void (*ErrorExitCb)(void);
|
||||
|
||||
private:
|
||||
static bool s_describedWarnings; // Told user how to disable warns
|
||||
@@ -192,6 +195,8 @@ class V3Error {
|
||||
static V3ErrorCode s_errorCode; // Error string being formed will abort
|
||||
static bool s_errorSuppressed; // Error being formed should be suppressed
|
||||
static MessagesSet s_messages; // What errors we've outputted
|
||||
static ErrorExitCb s_errorExitCb; // Callback when error occurs for dumping
|
||||
|
||||
enum MaxErrors { MAX_ERRORS = 50 }; // Fatal after this may errors
|
||||
|
||||
V3Error() { cerr<<("Static class"); abort(); }
|
||||
@@ -216,6 +221,7 @@ class V3Error {
|
||||
static bool isError(V3ErrorCode code, bool supp);
|
||||
static string lineStr (const char* filename, int lineno);
|
||||
static V3ErrorCode errorCode() { return s_errorCode; }
|
||||
static void errorExitCb(ErrorExitCb cb) { s_errorExitCb = cb; }
|
||||
|
||||
// When printing an error/warning, print prefix for multiline message
|
||||
static string warnMore();
|
||||
|
||||
+321
-20
@@ -41,6 +41,7 @@
|
||||
#include "V3Graph.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3Hashed.h"
|
||||
|
||||
typedef list<AstNodeVarRef*> GateVarRefList;
|
||||
|
||||
@@ -55,21 +56,34 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
class GateLogicVertex;
|
||||
class GateVarVertex;
|
||||
class GateGraphBaseVisitor {
|
||||
public:
|
||||
virtual AstNUser* visit(GateLogicVertex* vertexp, AstNUser* vup=NULL) =0;
|
||||
virtual AstNUser* visit(GateVarVertex* vertexp, AstNUser* vup=NULL) =0;
|
||||
virtual ~GateGraphBaseVisitor() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Support classes
|
||||
|
||||
class GateEitherVertex : public V3GraphVertex {
|
||||
AstScope* m_scopep;
|
||||
bool m_reducible; // True if this node should be able to be eliminated
|
||||
bool m_dedupable; // True if this node should be able to be deduped
|
||||
bool m_consumed; // Output goes to something meaningful
|
||||
public:
|
||||
GateEitherVertex(V3Graph* graphp, AstScope* scopep)
|
||||
: V3GraphVertex(graphp), m_scopep(scopep), m_reducible(true), m_consumed(false) {}
|
||||
: V3GraphVertex(graphp), m_scopep(scopep), m_reducible(true), m_dedupable(true), m_consumed(false) {}
|
||||
virtual ~GateEitherVertex() {}
|
||||
// Accessors
|
||||
// ACCESSORS
|
||||
virtual string dotStyle() const { return m_consumed?"":"dotted"; }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
bool reducible() const { return m_reducible; }
|
||||
bool dedupable() const { return m_dedupable; }
|
||||
void setConsumed(const char* consumedReason) {
|
||||
m_consumed = true;
|
||||
//UINFO(0,"\t\tSetConsumed "<<consumedReason<<" "<<this<<endl);
|
||||
@@ -79,6 +93,23 @@ public:
|
||||
m_reducible = false;
|
||||
//UINFO(0," NR: "<<nonReducibleReason<<" "<<name()<<endl);
|
||||
}
|
||||
void clearDedupable(const char* nonDedupableReason) {
|
||||
m_dedupable = false;
|
||||
//UINFO(0," ND: "<<nonDedupableReason<<" "<<name()<<endl);
|
||||
}
|
||||
void clearReducibleAndDedupable(const char* nonReducibleReason) {
|
||||
clearReducible(nonReducibleReason);
|
||||
clearDedupable(nonReducibleReason);
|
||||
}
|
||||
virtual AstNUser* accept(GateGraphBaseVisitor& v, AstNUser* vup=NULL) =0;
|
||||
// Returns only the result from the LAST vertex iterated over
|
||||
AstNUser* iterateInEdges(GateGraphBaseVisitor& v, AstNUser* vup=NULL) {
|
||||
AstNUser* retp = NULL;
|
||||
for (V3GraphEdge* edgep = inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
retp = dynamic_cast<GateEitherVertex*>(edgep->fromp())->accept(v, vup);
|
||||
}
|
||||
return retp;
|
||||
}
|
||||
};
|
||||
|
||||
class GateVarVertex : public GateEitherVertex {
|
||||
@@ -92,7 +123,7 @@ public:
|
||||
: GateEitherVertex(graphp, scopep), m_varScp(varScp), m_isTop(false)
|
||||
, m_isClock(false), m_rstSyncNodep(NULL), m_rstAsyncNodep(NULL) {}
|
||||
virtual ~GateVarVertex() {}
|
||||
// Accessors
|
||||
// ACCESSORS
|
||||
AstVarScope* varScp() const { return m_varScp; }
|
||||
virtual string name() const { return (cvtToStr((void*)m_varScp)+" "+varScp()->name()); }
|
||||
virtual string dotColor() const { return "blue"; }
|
||||
@@ -104,6 +135,16 @@ public:
|
||||
void rstSyncNodep(AstNode* nodep) { m_rstSyncNodep=nodep; }
|
||||
AstNode* rstAsyncNodep() const { return m_rstAsyncNodep; }
|
||||
void rstAsyncNodep(AstNode* nodep) { m_rstAsyncNodep=nodep; }
|
||||
// METHODS
|
||||
void propagateAttrClocksFrom(GateVarVertex* fromp) {
|
||||
// Propagate clock and general attribute onto this node
|
||||
varScp()->varp()->propagateAttrFrom(fromp->varScp()->varp());
|
||||
if (fromp->isClock()) {
|
||||
varScp()->varp()->usedClock(true);
|
||||
setIsClock();
|
||||
}
|
||||
}
|
||||
AstNUser* accept(GateGraphBaseVisitor& v, AstNUser* vup=NULL) { return v.visit(this,vup); }
|
||||
};
|
||||
|
||||
class GateLogicVertex : public GateEitherVertex {
|
||||
@@ -114,12 +155,13 @@ public:
|
||||
GateLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, AstActive* activep, bool slow)
|
||||
: GateEitherVertex(graphp,scopep), m_nodep(nodep), m_activep(activep), m_slow(slow) {}
|
||||
virtual ~GateLogicVertex() {}
|
||||
// Accessors
|
||||
// ACCESSORS
|
||||
virtual string name() const { return (cvtToStr((void*)m_nodep)+"@"+scopep()->prettyName()); }
|
||||
virtual string dotColor() const { return "yellow"; }
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
AstActive* activep() const { return m_activep; }
|
||||
bool slow() const { return m_slow; }
|
||||
AstNUser* accept(GateGraphBaseVisitor& v, AstNUser* vup=NULL) { return v.visit(this,vup); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -134,6 +176,7 @@ private:
|
||||
// STATE
|
||||
bool m_buffersOnly; // Set when we only allow simple buffering, no equations (for clocks)
|
||||
AstNodeVarRef* m_lhsVarRef; // VarRef on lhs of assignment (what we're replacing)
|
||||
bool m_dedupe; // Set when we use isGateDedupable instead of isGateOptimizable
|
||||
|
||||
// METHODS
|
||||
void clearSimple(const char* because) {
|
||||
@@ -172,7 +215,7 @@ private:
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
m_substTreep = nodep->rhsp();
|
||||
if (!nodep->lhsp()->castNodeVarRef())
|
||||
clearSimple("ASSIGN(non VARREF)");
|
||||
clearSimple("ASSIGN(non-VARREF)");
|
||||
else nodep->iterateChildren(*this);
|
||||
// We don't push logic other then assignments/NOTs into SenItems
|
||||
// This avoids a mess in computing what exactly a POSEDGE is
|
||||
@@ -193,7 +236,7 @@ private:
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
// *** Special iterator
|
||||
if (!m_isSimple) return; // Fastpath
|
||||
if (!nodep->isGateOptimizable()
|
||||
if (!(m_dedupe ? nodep->isGateDedupable() : nodep->isGateOptimizable())
|
||||
|| !nodep->isPure()
|
||||
|| nodep->isBrancher()) {
|
||||
UINFO(5, "Non optimizable type: "<<nodep<<endl);
|
||||
@@ -203,11 +246,12 @@ private:
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
GateOkVisitor(AstNode* nodep, bool buffersOnly) {
|
||||
GateOkVisitor(AstNode* nodep, bool buffersOnly, bool dedupe) {
|
||||
m_isSimple = true;
|
||||
m_substTreep = NULL;
|
||||
m_buffersOnly = buffersOnly;
|
||||
m_lhsVarRef = NULL;
|
||||
m_dedupe = dedupe;
|
||||
// Iterate
|
||||
nodep->accept(*this);
|
||||
// Check results
|
||||
@@ -258,6 +302,7 @@ private:
|
||||
bool m_inSlow; // Inside a slow structure
|
||||
V3Double0 m_statSigs; // Statistic tracking
|
||||
V3Double0 m_statRefs; // Statistic tracking
|
||||
V3Double0 m_statDedupLogic; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
void iterateNewStmt(AstNode* nodep, const char* nonReducibleReason, const char* consumeReason) {
|
||||
@@ -265,9 +310,10 @@ private:
|
||||
UINFO(4," STMT "<<nodep<<endl);
|
||||
// m_activep is null under AstCFunc's, that's ok.
|
||||
m_logicVertexp = new GateLogicVertex(&m_graph, m_scopep, nodep, m_activep, m_inSlow);
|
||||
if (!m_activeReducible) nonReducibleReason="Block Unreducible";
|
||||
if (nonReducibleReason) {
|
||||
m_logicVertexp->clearReducible(nonReducibleReason);
|
||||
m_logicVertexp->clearReducibleAndDedupable(nonReducibleReason);
|
||||
} else if (!m_activeReducible) {
|
||||
m_logicVertexp->clearReducible("Block Unreducible"); // Sequential logic is dedupable
|
||||
}
|
||||
if (consumeReason) m_logicVertexp->setConsumed(consumeReason);
|
||||
if (nodep->castSenItem()) m_logicVertexp->setConsumed("senItem");
|
||||
@@ -284,13 +330,13 @@ private:
|
||||
varscp->user1p(vertexp);
|
||||
if (varscp->varp()->isSigPublic()) {
|
||||
// Public signals shouldn't be changed, pli code might be messing with them
|
||||
vertexp->clearReducible("SigPublic");
|
||||
vertexp->clearReducibleAndDedupable("SigPublic");
|
||||
vertexp->setConsumed("SigPublic");
|
||||
}
|
||||
if (varscp->varp()->isIO() && varscp->scopep()->isTop()) {
|
||||
// We may need to convert to/from sysc/reg sigs
|
||||
vertexp->setIsTop();
|
||||
vertexp->clearReducible("isTop");
|
||||
vertexp->clearReducibleAndDedupable("isTop");
|
||||
vertexp->setConsumed("isTop");
|
||||
}
|
||||
if (varscp->varp()->isUsedClock()) vertexp->setConsumed("clock");
|
||||
@@ -305,6 +351,7 @@ private:
|
||||
void consumedMarkRecurse(GateEitherVertex* vertexp);
|
||||
void consumedMove();
|
||||
void replaceAssigns();
|
||||
void dedupe();
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep, AstNUser*) {
|
||||
@@ -319,6 +366,8 @@ private:
|
||||
optimizeSignals(false);
|
||||
// Then propagate more complicated equations
|
||||
optimizeSignals(true);
|
||||
// Remove redundant logic
|
||||
if (v3Global.opt.oDedupe()) dedupe();
|
||||
// Warn
|
||||
warnSignals();
|
||||
consumedMark();
|
||||
@@ -443,6 +492,7 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
GateVisitor(AstNode* nodep) {
|
||||
AstNode::user1ClearTree();
|
||||
m_logicVertexp = NULL;
|
||||
m_scopep = NULL;
|
||||
m_modp = NULL;
|
||||
@@ -455,6 +505,7 @@ public:
|
||||
virtual ~GateVisitor() {
|
||||
V3Stats::addStat("Optimizations, Gate sigs deleted", m_statSigs);
|
||||
V3Stats::addStat("Optimizations, Gate inputs replaced", m_statRefs);
|
||||
V3Stats::addStat("Optimizations, Gate sigs deduped", m_statDedupLogic);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -464,7 +515,7 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
if (vvertexp->inEmpty()) {
|
||||
vvertexp->clearReducible("inEmpty"); // Can't deal with no sources
|
||||
vvertexp->clearReducibleAndDedupable("inEmpty"); // Can't deal with no sources
|
||||
if (!vvertexp->isTop() // Ok if top inputs are driverless
|
||||
&& !vvertexp->varScp()->varp()->valuep()
|
||||
&& !vvertexp->varScp()->varp()->isSigPublic()) {
|
||||
@@ -480,7 +531,7 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
||||
}
|
||||
}
|
||||
else if (!vvertexp->inSize1()) {
|
||||
vvertexp->clearReducible("size!1"); // Can't deal with more than one src
|
||||
vvertexp->clearReducibleAndDedupable("size!1"); // Can't deal with more than one src
|
||||
}
|
||||
// Reduce it?
|
||||
if (!vvertexp->reducible()) {
|
||||
@@ -493,7 +544,7 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
||||
AstNode* logicp = logicVertexp->nodep();
|
||||
if (logicVertexp->reducible()) {
|
||||
// Can we eliminate?
|
||||
GateOkVisitor okVisitor(logicp, vvertexp->isClock());
|
||||
GateOkVisitor okVisitor(logicp, vvertexp->isClock(), false);
|
||||
bool multiInputs = okVisitor.rhsVarRefs().size() > 1;
|
||||
// Was it ok?
|
||||
bool doit = okVisitor.isSimple();
|
||||
@@ -548,12 +599,8 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
||||
UINFO(9," Point-to-new vertex "<<newvarscp<<endl);
|
||||
GateVarVertex* varvertexp = makeVarVertex(newvarscp);
|
||||
new V3GraphEdge(&m_graph, varvertexp, consumeVertexp, 1);
|
||||
newvarscp->varp()->propagateAttrFrom(vvertexp->varScp()->varp());
|
||||
if (vvertexp->isClock()) {
|
||||
// Propagate clock attribute onto generating node
|
||||
newvarscp->varp()->usedClock(true);
|
||||
varvertexp->setIsClock();
|
||||
}
|
||||
// Propagate clock attribute onto generating node
|
||||
varvertexp->propagateAttrClocksFrom(vvertexp);
|
||||
}
|
||||
// Remove the edge
|
||||
edgep->unlinkDelete(); edgep=NULL;
|
||||
@@ -725,6 +772,8 @@ private:
|
||||
// However a VARREF should point to the original as it's otherwise confusing
|
||||
// to throw warnings that point to a PIN rather than where the pin us used.
|
||||
if (substp->castVarRef()) substp->fileline(nodep->fileline());
|
||||
// Make the substp an rvalue like nodep. This facilitate the hashing in dedupe.
|
||||
if (AstNodeVarRef* varrefp = substp->castNodeVarRef()) varrefp->lvalue(false);
|
||||
nodep->replaceWith(substp);
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
}
|
||||
@@ -756,6 +805,258 @@ void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode*
|
||||
}
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Auxiliary hash class for GateDedupeVarVisitor
|
||||
|
||||
class GateDedupeHash : public V3HashedUserCheck {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Ast*::user2p -> parent AstNodeAssign* for this rhsp
|
||||
// Ast*::user3p -> AstNode* checked in test for duplicate
|
||||
// Ast*::user5p -> AstNode* checked in test for duplicate
|
||||
// AstUser2InUse m_inuser2; (Allocated for use in GateVisitor)
|
||||
AstUser3InUse m_inuser3;
|
||||
AstUser5InUse m_inuser5;
|
||||
V3Hashed m_hashed; // Hash, contains rhs of assigns
|
||||
|
||||
void hash(AstNode* nodep) {
|
||||
// !NULL && the object is hashable
|
||||
if (nodep && !nodep->sameHash().isIllegal()) {
|
||||
m_hashed.hash(nodep);
|
||||
}
|
||||
}
|
||||
bool sameHash(AstNode* node1p, AstNode* node2p) {
|
||||
return (node1p && node2p
|
||||
&& !node1p->sameHash().isIllegal()
|
||||
&& !node2p->sameHash().isIllegal()
|
||||
&& m_hashed.sameNodes(node1p,node2p));
|
||||
}
|
||||
bool same(AstNUser* node1p, AstNUser* node2p) {
|
||||
return node1p == node2p || sameHash((AstNode*)node1p,(AstNode*)node2p);
|
||||
}
|
||||
public:
|
||||
bool check(AstNode* node1p,AstNode* node2p) {
|
||||
return same(node1p->user3p(),node2p->user3p()) && same(node1p->user5p(),node2p->user5p())
|
||||
&& node1p->user2p()->castNode()->type() == node2p->user2p()->castNode()->type()
|
||||
;
|
||||
}
|
||||
|
||||
AstNodeAssign* hashAndFindDupe(AstNodeAssign* assignp, AstNode* extra1p, AstNode* extra2p) {
|
||||
AstNode *rhsp = assignp->rhsp();
|
||||
rhsp->user2p(assignp);
|
||||
rhsp->user3p(extra1p);
|
||||
rhsp->user5p(extra2p);
|
||||
|
||||
hash(extra1p);
|
||||
hash(extra2p);
|
||||
|
||||
V3Hashed::iterator inserted = m_hashed.hashAndInsert(rhsp);
|
||||
V3Hashed::iterator dupit = m_hashed.findDuplicate(rhsp, this);
|
||||
// Even though rhsp was just inserted, V3Hashed::findDuplicate doesn't
|
||||
// return anything in the hash that has the same pointer (V3Hashed.cpp::findDuplicate)
|
||||
// So dupit is either a different, duplicate rhsp, or the end of the hash.
|
||||
if (dupit != m_hashed.end()) {
|
||||
m_hashed.erase(inserted);
|
||||
return m_hashed.iteratorNodep(dupit)->user2p()->castNode()->castNodeAssign();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Have we seen the rhs of this assign before?
|
||||
|
||||
class GateDedupeVarVisitor : public GateBaseVisitor {
|
||||
// Given a node, it is visited to try to find the AstNodeAssign under it that can used for dedupe.
|
||||
// Right now, only the following node trees are supported for dedupe.
|
||||
// 1. AstNodeAssign
|
||||
// 2. AstAlways -> AstNodeAssign
|
||||
// (Note, the assign must also be the only node under the always)
|
||||
// 3. AstAlways -> AstNodeIf -> AstNodeAssign
|
||||
// (Note, the IF must be the only node under the always,
|
||||
// and the assign must be the only node under the if, other than the ifcond)
|
||||
// Any other ordering or node type, except for an AstComment, makes it not dedupable
|
||||
private:
|
||||
// STATE
|
||||
GateDedupeHash m_hash; // Hash used to find dupes of rhs of assign
|
||||
AstNodeAssign* m_assignp; // Assign found for dedupe
|
||||
AstNode* m_ifCondp; // IF condition that assign is under
|
||||
bool m_always; // Assign is under an always
|
||||
bool m_dedupable; // Determined the assign to be dedupable
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeAssign* assignp, AstNUser*) {
|
||||
if (m_dedupable) {
|
||||
// I think we could safely dedupe an always block with multiple non-blocking statements, but erring on side of caution here
|
||||
if (!m_assignp) {
|
||||
m_assignp = assignp;
|
||||
} else {
|
||||
m_dedupable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAlways* alwaysp, AstNUser*) {
|
||||
if (m_dedupable) {
|
||||
if (!m_always) {
|
||||
m_always = true;
|
||||
alwaysp->bodysp()->iterateAndNext(*this);
|
||||
} else {
|
||||
m_dedupable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ugly support for latches of the specific form -
|
||||
// always @(...)
|
||||
// if (...)
|
||||
// foo = ...; // or foo <= ...;
|
||||
virtual void visit(AstNodeIf* ifp, AstNUser*) {
|
||||
if (m_dedupable) {
|
||||
if (m_always && !m_ifCondp && !ifp->elsesp()) { //we're under an always, this is the first IF, and there's no else
|
||||
m_ifCondp = ifp->condp();
|
||||
ifp->ifsp()->iterateAndNext(*this);
|
||||
} else {
|
||||
m_dedupable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstComment*, AstNUser*) {} // NOP
|
||||
//--------------------
|
||||
// Default
|
||||
virtual void visit(AstNode*, AstNUser*) {
|
||||
m_dedupable = false;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
GateDedupeVarVisitor() {}
|
||||
// PUBLIC METHODS
|
||||
AstNodeVarRef* findDupe(AstNode* nodep, AstVarScope* consumerVarScopep, AstActive* activep) {
|
||||
m_assignp = NULL;
|
||||
m_ifCondp = NULL;
|
||||
m_always = false;
|
||||
m_dedupable = true;
|
||||
nodep->accept(*this);
|
||||
if (m_dedupable && m_assignp) {
|
||||
AstNode* lhsp = m_assignp->lhsp();
|
||||
// Possible todo, handle more complex lhs expressions
|
||||
if (AstNodeVarRef* lhsVarRefp = lhsp->castNodeVarRef()) {
|
||||
if (lhsVarRefp->varScopep() != consumerVarScopep) consumerVarScopep->v3fatalSrc("Consumer doesn't match lhs of assign");
|
||||
if (AstNodeAssign* dup = m_hash.hashAndFindDupe(m_assignp,activep,m_ifCondp)) {
|
||||
return (AstNodeVarRef*) dup->lhsp();
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Recurse through the graph, looking for duplicate expressions on the rhs of an assign
|
||||
|
||||
class GateDedupeGraphVisitor : public GateGraphBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstVarScope::user2p -> bool: already visited
|
||||
// AstUser2InUse m_inuser2; (Allocated for use in GateVisitor)
|
||||
V3Double0 m_numDeduped; // Statistic tracking
|
||||
GateDedupeVarVisitor m_varVisitor; // Looks for a dupe of the logic
|
||||
|
||||
virtual AstNUser* visit(GateVarVertex *vvertexp, AstNUser*) {
|
||||
// Check that we haven't been here before
|
||||
if (vvertexp->varScp()->user2()) return NULL;
|
||||
vvertexp->varScp()->user2(true);
|
||||
|
||||
AstNodeVarRef* dupVarRefp = (AstNodeVarRef*) vvertexp->iterateInEdges(*this, (AstNUser*) vvertexp);
|
||||
|
||||
if (dupVarRefp && vvertexp->inSize1()) {
|
||||
V3GraphEdge* edgep = vvertexp->inBeginp();
|
||||
GateLogicVertex* lvertexp = (GateLogicVertex*)edgep->fromp();
|
||||
if (!vvertexp->dedupable()) vvertexp->varScp()->v3fatalSrc("GateLogicVertex* visit should have returned NULL if consumer var vertex is not dedupable.");
|
||||
GateOkVisitor okVisitor(lvertexp->nodep(), false, true);
|
||||
if (okVisitor.isSimple()) {
|
||||
AstVarScope* dupVarScopep = dupVarRefp->varScopep();
|
||||
GateVarVertex* dupVvertexp = (GateVarVertex*) (dupVarScopep->user1p());
|
||||
UINFO(4,"replacing " << vvertexp << " with " << dupVvertexp << endl);
|
||||
m_numDeduped++;
|
||||
// Replace all of this varvertex's consumers with dupVarRefp
|
||||
for (V3GraphEdge* outedgep = vvertexp->outBeginp();outedgep;) {
|
||||
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(outedgep->top());
|
||||
AstNode* consumerp = consumeVertexp->nodep();
|
||||
GateElimVisitor elimVisitor(consumerp,vvertexp->varScp(),dupVarRefp);
|
||||
outedgep = outedgep->relinkFromp(dupVvertexp);
|
||||
}
|
||||
|
||||
// Propogate attributes
|
||||
dupVvertexp->propagateAttrClocksFrom(vvertexp);
|
||||
|
||||
// Remove inputs links
|
||||
while (V3GraphEdge* inedgep = vvertexp->inBeginp()) {
|
||||
inedgep->unlinkDelete(); inedgep=NULL;
|
||||
}
|
||||
// replaceAssigns() does the deleteTree on lvertexNodep in a later step
|
||||
AstNode* lvertexNodep = lvertexp->nodep();
|
||||
lvertexNodep->unlinkFrBack();
|
||||
vvertexp->varScp()->valuep(lvertexNodep);
|
||||
lvertexNodep = NULL;
|
||||
vvertexp->user(true);
|
||||
lvertexp->user(true);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Returns a varref that has the same logic input
|
||||
virtual AstNUser* visit(GateLogicVertex* lvertexp, AstNUser* vup) {
|
||||
lvertexp->iterateInEdges(*this);
|
||||
|
||||
GateVarVertex* consumerVvertexpp = (GateVarVertex*) vup;
|
||||
if (lvertexp->dedupable() && consumerVvertexpp->dedupable()) {
|
||||
AstNode* nodep = lvertexp->nodep();
|
||||
AstVarScope* consumerVarScopep = consumerVvertexpp->varScp();
|
||||
// TODO: Doing a simple pointer comparison of activep won't work
|
||||
// optimally for statements under generated clocks. Statements under
|
||||
// different generated clocks will never compare as equal, even if the
|
||||
// generated clocks are deduped into one clock.
|
||||
AstActive* activep = lvertexp->activep();
|
||||
return (AstNUser*) m_varVisitor.findDupe(nodep, consumerVarScopep, activep);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
GateDedupeGraphVisitor() {}
|
||||
void dedupeTree(GateVarVertex* vvertexp) {
|
||||
vvertexp->accept(*this);
|
||||
}
|
||||
V3Double0 numDeduped() { return m_numDeduped; }
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void GateVisitor::dedupe() {
|
||||
AstNode::user2ClearTree();
|
||||
GateDedupeGraphVisitor deduper;
|
||||
// Traverse starting from each of the clocks
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
if (vvertexp->isClock()) {
|
||||
deduper.dedupeTree(vvertexp);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Traverse starting from each of the outputs
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
|
||||
if (vvertexp->isTop() && vvertexp->varScp()->varp()->isOutput()) {
|
||||
deduper.dedupeTree(vvertexp);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_statDedupLogic += deduper.numDeduped();
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Convert VARSCOPE(ASSIGN(default, VARREF)) to just VARSCOPE(default)
|
||||
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ public:
|
||||
string debugFilename(const string& nameComment, int newNumber=0) {
|
||||
++m_debugFileNumber;
|
||||
if (newNumber) m_debugFileNumber = newNumber;
|
||||
char digits[100]; sprintf(digits, "%02d", m_debugFileNumber);
|
||||
char digits[100]; sprintf(digits, "%03d", m_debugFileNumber);
|
||||
return opt.makeDir()+"/"+opt.prefix()+"_"+digits+"_"+nameComment;
|
||||
}
|
||||
bool needHInlines() const { return m_needHInlines; }
|
||||
|
||||
+23
-5
@@ -39,9 +39,14 @@ int V3Graph::debug() { return max(V3Error::debugDefault(), s_debug); }
|
||||
//######################################################################
|
||||
// Vertices
|
||||
|
||||
V3GraphVertex::V3GraphVertex(V3Graph* graphp, const V3GraphVertex& old)
|
||||
: m_fanout(old.m_fanout), m_color(old.m_color), m_rank(old.m_rank) {
|
||||
m_userp = NULL;
|
||||
verticesPushBack(graphp);
|
||||
}
|
||||
|
||||
V3GraphVertex::V3GraphVertex(V3Graph* graphp)
|
||||
: m_fanout(0), m_color(0), m_rank(0)
|
||||
{
|
||||
: m_fanout(0), m_color(0), m_rank(0) {
|
||||
m_userp = NULL;
|
||||
verticesPushBack(graphp);
|
||||
}
|
||||
@@ -125,9 +130,9 @@ ostream& operator<<(ostream& os, V3GraphVertex* vertexp) {
|
||||
//######################################################################
|
||||
// Edges
|
||||
|
||||
V3GraphEdge::V3GraphEdge(V3Graph* graphp,
|
||||
V3GraphVertex* fromp, V3GraphVertex* top, int weight,
|
||||
bool cutable) {
|
||||
void V3GraphEdge::init(V3Graph* graphp,
|
||||
V3GraphVertex* fromp, V3GraphVertex* top, int weight,
|
||||
bool cutable) {
|
||||
UASSERT(fromp, "Null from pointer\n");
|
||||
UASSERT(top, "Null to pointer\n");
|
||||
m_fromp = fromp;
|
||||
@@ -140,6 +145,14 @@ V3GraphEdge::V3GraphEdge(V3Graph* graphp,
|
||||
inPushBack();
|
||||
}
|
||||
|
||||
V3GraphEdge* V3GraphEdge::relinkFromp(V3GraphVertex* newFromp) {
|
||||
V3GraphEdge *oldNxt = outNextp();
|
||||
m_outs.unlink(m_fromp->m_outs, this);
|
||||
m_fromp = newFromp;
|
||||
outPushBack();
|
||||
return oldNxt;
|
||||
}
|
||||
|
||||
void V3GraphEdge::unlinkDelete() {
|
||||
// Unlink from side
|
||||
m_outs.unlink(m_fromp->m_outs, this);
|
||||
@@ -267,6 +280,11 @@ void V3Graph::dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgrap
|
||||
}
|
||||
}
|
||||
|
||||
//! Variant of dumpDotFilePrefixed without --dump option check
|
||||
void V3Graph::dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsSubgraph) {
|
||||
dumpDotFile(v3Global.debugFilename(nameComment)+".dot", colorAsSubgraph);
|
||||
}
|
||||
|
||||
void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) {
|
||||
// This generates a file used by graphviz, http://www.graphviz.org
|
||||
// "hardcoded" parameters:
|
||||
|
||||
+29
-7
@@ -119,13 +119,17 @@ public:
|
||||
/// Remove any redundant edges, weights become SUM of any other weight
|
||||
void removeRedundantEdgesSum(V3EdgeFuncP edgeFuncp);
|
||||
|
||||
/// Call loopsVertexCb on any loops starting where specified
|
||||
/// Call loopsVertexCb on any one loop starting where specified
|
||||
void reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp);
|
||||
|
||||
/// Build a subgraph of all loops starting where specified
|
||||
void subtreeLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp, V3Graph* loopGraphp);
|
||||
|
||||
/// Debugging
|
||||
void dump(ostream& os=cout);
|
||||
void dumpDotFile(const string& filename, bool colorAsSubgraph);
|
||||
void dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph=false);
|
||||
void dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsSubgraph=false);
|
||||
void userClearVertices();
|
||||
void userClearEdges();
|
||||
static void test();
|
||||
@@ -159,12 +163,18 @@ protected:
|
||||
void rank(uint32_t rank) { m_rank = rank; }
|
||||
void inUnlink() { m_ins.reset(); } // Low level; normally unlinkDelete is what you want
|
||||
void outUnlink() { m_outs.reset(); } // Low level; normally unlinkDelete is what you want
|
||||
protected:
|
||||
// CONSTRUCTORS
|
||||
V3GraphVertex(V3Graph* graphp, const V3GraphVertex& old);
|
||||
public:
|
||||
// CONSTRUCTION
|
||||
V3GraphVertex(V3Graph* graphp);
|
||||
//! Clone copy constructor. Doesn't copy edges or user/userp.
|
||||
virtual V3GraphVertex* clone(V3Graph* graphp) const {
|
||||
return new V3GraphVertex(graphp, *this); }
|
||||
virtual ~V3GraphVertex() {}
|
||||
void unlinkEdges(V3Graph* graphp);
|
||||
void unlinkDelete(V3Graph* graphp);
|
||||
|
||||
// ACCESSORS
|
||||
virtual string name() const { return ""; }
|
||||
virtual string dotColor() const { return "black"; }
|
||||
@@ -207,6 +217,9 @@ ostream& operator<<(ostream& os, V3GraphVertex* vertexp);
|
||||
|
||||
class V3GraphEdge {
|
||||
// Wires/variables aren't edges. Edges have only a single to/from vertex
|
||||
public:
|
||||
// ENUMS
|
||||
enum Cuttable { NOT_CUTABLE = false, CUTABLE = true }; // For passing to V3GraphEdge
|
||||
protected:
|
||||
friend class V3Graph; friend class V3GraphVertex;
|
||||
friend class GraphAcyc; friend class GraphAcycEdge;
|
||||
@@ -222,15 +235,23 @@ protected:
|
||||
uint32_t m_user; // Marker for some algorithms
|
||||
};
|
||||
// METHODS
|
||||
void init(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cutable=false);
|
||||
void cut() { m_weight = 0; } // 0 weight is same as disconnected
|
||||
void outPushBack();
|
||||
void inPushBack();
|
||||
// CONSTRUCTORS
|
||||
protected:
|
||||
V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, const V3GraphEdge& old) {
|
||||
init(graphp, fromp, top, old.m_weight, old.m_cutable);
|
||||
}
|
||||
public:
|
||||
// ENUMS
|
||||
enum Cuttable { NOT_CUTABLE = false, CUTABLE = true }; // For passing to V3GraphEdge
|
||||
// CONSTRUCTION
|
||||
// Add DAG from one node to the specified node
|
||||
V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cutable=false);
|
||||
//! Add DAG from one node to the specified node
|
||||
V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cutable=false) {
|
||||
init(graphp, fromp, top, weight, cutable);
|
||||
}
|
||||
//! Clone copy constructor. Doesn't copy existing vertices or user/userp.
|
||||
virtual V3GraphEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new V3GraphEdge(graphp, fromp, top, *this); }
|
||||
virtual ~V3GraphEdge() {}
|
||||
// METHODS
|
||||
virtual string name() const { return m_fromp->name()+"->"+m_top->name(); }
|
||||
@@ -242,6 +263,7 @@ public:
|
||||
return top()->sortCmp(rhsp->top());
|
||||
}
|
||||
void unlinkDelete();
|
||||
V3GraphEdge* relinkFromp(V3GraphVertex* newFromp);
|
||||
// ACCESSORS
|
||||
int weight() const { return m_weight; }
|
||||
void weight(int weight) { m_weight=weight; }
|
||||
|
||||
+1
-1
@@ -461,7 +461,7 @@ void GraphAcyc::place() {
|
||||
}
|
||||
|
||||
// Sort by weight, then by vertex (so that we completely process one vertex, when possible)
|
||||
sort(edges.begin(), edges.end(), GraphAcycEdgeCmp());
|
||||
stable_sort(edges.begin(), edges.end(), GraphAcycEdgeCmp());
|
||||
|
||||
// Process each edge in weighted order
|
||||
m_placeStep = 10;
|
||||
|
||||
+57
-3
@@ -376,6 +376,58 @@ void V3Graph::reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp) {
|
||||
GraphAlgRLoops (this, edgeFuncp, vertexp);
|
||||
}
|
||||
|
||||
|
||||
//######################################################################
|
||||
//######################################################################
|
||||
// Algorithms - subtrees
|
||||
|
||||
class GraphAlgSubtrees : GraphAlg {
|
||||
private:
|
||||
V3Graph* m_loopGraphp;
|
||||
|
||||
//! Iterate through all connected nodes of a graph with a loop or loops.
|
||||
V3GraphVertex* vertexIterateAll(V3GraphVertex* vertexp) {
|
||||
if (V3GraphVertex* newVertexp = (V3GraphVertex*)vertexp->userp()) {
|
||||
return newVertexp;
|
||||
} else {
|
||||
newVertexp = vertexp->clone(m_loopGraphp);
|
||||
vertexp->userp(newVertexp);
|
||||
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp();
|
||||
edgep; edgep=edgep->outNextp()) {
|
||||
if (followEdge(edgep)) {
|
||||
V3GraphEdge* newEdgep = (V3GraphEdge*)edgep->userp();
|
||||
if (!newEdgep) {
|
||||
V3GraphVertex* newTop = vertexIterateAll(edgep->top());
|
||||
newEdgep = edgep->clone(m_loopGraphp, newVertexp,
|
||||
newTop);
|
||||
edgep->userp(newEdgep);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newVertexp;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
GraphAlgSubtrees(V3Graph* graphp, V3Graph* loopGraphp,
|
||||
V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp)
|
||||
: GraphAlg(graphp, edgeFuncp), m_loopGraphp (loopGraphp) {
|
||||
// Vertex::m_userp - New vertex if we have seen this vertex already
|
||||
// Edge::m_userp - New edge if we have seen this edge already
|
||||
m_graphp->userClearVertices();
|
||||
m_graphp->userClearEdges();
|
||||
(void) vertexIterateAll(vertexp);
|
||||
}
|
||||
~GraphAlgSubtrees() {}
|
||||
};
|
||||
|
||||
//! Report the entire connected graph with a loop or loops
|
||||
void V3Graph::subtreeLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp,
|
||||
V3Graph* loopGraphp) {
|
||||
GraphAlgSubtrees (this, loopGraphp, edgeFuncp, vertexp);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
//######################################################################
|
||||
// Algorithms - make non cutable
|
||||
@@ -412,7 +464,7 @@ void V3Graph::sortVertices() {
|
||||
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
|
||||
vertices.push_back(vertexp);
|
||||
}
|
||||
std::sort(vertices.begin(), vertices.end(), GraphSortVertexCmp());
|
||||
std::stable_sort(vertices.begin(), vertices.end(), GraphSortVertexCmp());
|
||||
this->verticesUnlink();
|
||||
for (vector<V3GraphVertex*>::iterator it = vertices.begin(); it!=vertices.end(); ++it) {
|
||||
(*it)->verticesPushBack(this);
|
||||
@@ -428,7 +480,7 @@ void V3Graph::sortEdges() {
|
||||
edges.push_back(edgep);
|
||||
}
|
||||
// Sort
|
||||
std::sort(edges.begin(), edges.end(), GraphSortEdgeCmp());
|
||||
std::stable_sort(edges.begin(), edges.end(), GraphSortEdgeCmp());
|
||||
|
||||
// Relink edges in specified order
|
||||
// We know the vector contains all of the edges that were
|
||||
@@ -465,7 +517,9 @@ void V3Graph::order() {
|
||||
}
|
||||
}
|
||||
|
||||
// Sort list of vertices by rank, then fanout
|
||||
// Sort list of vertices by rank, then fanout. Fanout is a bit of a
|
||||
// misnomer. It is the sum of all the fanouts of nodes reached from a node
|
||||
// *plus* the count of edges in to that node.
|
||||
sortVertices();
|
||||
// Sort edges by rank then fanout of node they point to
|
||||
sortEdges();
|
||||
|
||||
+19
-2
@@ -105,12 +105,16 @@ public:
|
||||
//######################################################################
|
||||
// Hashed class functions
|
||||
|
||||
void V3Hashed::hashAndInsert(AstNode* nodep) {
|
||||
V3Hashed::iterator V3Hashed::hashAndInsert(AstNode* nodep) {
|
||||
hash(nodep);
|
||||
return m_hashMmap.insert(make_pair(nodeHash(nodep), nodep));
|
||||
}
|
||||
|
||||
void V3Hashed::hash(AstNode* nodep) {
|
||||
UINFO(8," hashI "<<nodep<<endl);
|
||||
if (!nodep->user4p()) {
|
||||
HashedVisitor visitor (nodep);
|
||||
}
|
||||
m_hashMmap.insert(make_pair(nodeHash(nodep), nodep));
|
||||
}
|
||||
|
||||
bool V3Hashed::sameNodes(AstNode* node1p, AstNode* node2p) {
|
||||
@@ -189,3 +193,16 @@ V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep) {
|
||||
return end();
|
||||
}
|
||||
|
||||
V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep, V3HashedUserCheck* checkp) {
|
||||
UINFO(8," findD "<<nodep<<endl);
|
||||
if (!nodep->user4p()) nodep->v3fatalSrc("Called findDuplicate on non-hashed node");
|
||||
pair <HashMmap::iterator,HashMmap::iterator> eqrange = mmap().equal_range(nodeHash(nodep));
|
||||
for (HashMmap::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (nodep != node2p && checkp->check(nodep,node2p) && sameNodes(nodep, node2p)) {
|
||||
return eqit;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -45,6 +45,13 @@ public:
|
||||
|
||||
//============================================================================
|
||||
|
||||
struct V3HashedUserCheck {
|
||||
// Functor for V3Hashed::findDuplicate
|
||||
virtual bool check(AstNode*,AstNode*) =0;
|
||||
V3HashedUserCheck() {}
|
||||
virtual ~V3HashedUserCheck() {}
|
||||
};
|
||||
|
||||
class V3Hashed : public VHashedBase {
|
||||
// NODE STATE
|
||||
// AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
|
||||
@@ -70,10 +77,12 @@ public:
|
||||
|
||||
// METHODS
|
||||
void clear() { m_hashMmap.clear(); AstNode::user4ClearTree(); }
|
||||
void hashAndInsert(AstNode* nodep); // Hash the node, and insert into map
|
||||
iterator hashAndInsert(AstNode* nodep); // Hash the node, and insert into map. Return iterator to inserted
|
||||
void hash(AstNode* nodep); // Only hash the node
|
||||
bool sameNodes(AstNode* node1p, AstNode* node2p); // After hashing, and tell if identical
|
||||
void erase(iterator it); // Remove node from structures
|
||||
iterator findDuplicate(AstNode* nodep); // Return duplicate in hash, if any
|
||||
iterator findDuplicate(AstNode* nodep, V3HashedUserCheck* checkp); // Extra user checks for sameness
|
||||
AstNode* iteratorNodep(iterator it) { return it->second; }
|
||||
void dumpFile(const string& filename, bool tree);
|
||||
void dumpFilePrefixed(const string& nameComment, bool tree=false);
|
||||
|
||||
+378
-278
@@ -42,11 +42,376 @@
|
||||
#include "V3Stats.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
// CONFIG
|
||||
static const int INLINE_MODS_SMALLER = 100; // If a mod is < this # nodes, can always inline it
|
||||
|
||||
//######################################################################
|
||||
// Inline state, as a visitor of each AstNode
|
||||
|
||||
// CONFIG
|
||||
static const int INLINE_MODS_SMALLER = 100; // If a mod is < this # nodes, can always inline it
|
||||
class InlineMarkVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Output
|
||||
// AstNodeModule::user1() // OUTPUT: bool. User request to inline this module
|
||||
// Entire netlist (can be cleared after this visit completes)
|
||||
// AstNodeModule::user2() // CIL_*. Allowed to automatically inline module
|
||||
// AstNodeModule::user3() // int. Number of cells referencing this module
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
|
||||
enum {CIL_NOTHARD=0, // For user2, inline not supported
|
||||
CIL_NOTSOFT, // For user2, don't inline unless user overrides
|
||||
CIL_MAYBE}; // For user2, might inline
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Flattened cell's containing module
|
||||
int m_stmtCnt; // Statements in module
|
||||
V3Double0 m_statUnsup; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
void cantInline(const char* reason, bool hard) {
|
||||
if (hard) {
|
||||
if (m_modp->user2() != CIL_NOTHARD) {
|
||||
UINFO(4," No inline hard: "<<reason<<" "<<m_modp<<endl);
|
||||
m_modp->user2(CIL_NOTHARD);
|
||||
m_statUnsup++;
|
||||
}
|
||||
} else {
|
||||
if (m_modp->user2() == CIL_MAYBE) {
|
||||
UINFO(4," No inline soft: "<<reason<<" "<<m_modp<<endl);
|
||||
m_modp->user2(CIL_NOTSOFT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||
m_stmtCnt = 0;
|
||||
m_modp = nodep;
|
||||
m_modp->user2(CIL_MAYBE);
|
||||
if (m_modp->castIface()) {
|
||||
// Inlining an interface means we no longer have a cell handle to resolve to.
|
||||
// If inlining moves post-scope this can perhaps be relaxed.
|
||||
cantInline("modIface",true);
|
||||
}
|
||||
if (m_modp->modPublic()) cantInline("modPublic",false);
|
||||
//
|
||||
nodep->iterateChildren(*this);
|
||||
//
|
||||
bool userinline = nodep->user1();
|
||||
int allowed = nodep->user2();
|
||||
int refs = nodep->user3();
|
||||
// Should we automatically inline this module?
|
||||
// inlineMult = 2000 by default. If a mod*#instances is < this # nodes, can inline it
|
||||
bool doit = ((allowed == CIL_NOTSOFT || allowed == CIL_MAYBE)
|
||||
&& (userinline
|
||||
|| ((allowed == CIL_MAYBE)
|
||||
&& (refs==1
|
||||
|| m_stmtCnt < INLINE_MODS_SMALLER
|
||||
|| v3Global.opt.inlineMult() < 1
|
||||
|| refs*m_stmtCnt < v3Global.opt.inlineMult()))));
|
||||
// Packages aren't really "under" anything so they confuse this algorithm
|
||||
if (nodep->castPackage()) doit = false;
|
||||
UINFO(4, " Inline="<<doit<<" Possible="<<allowed<<" Usr="<<userinline<<" Refs="<<refs<<" Stmts="<<m_stmtCnt
|
||||
<<" "<<nodep<<endl);
|
||||
nodep->user1(doit);
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstCell* nodep, AstNUser*) {
|
||||
nodep->modp()->user3Inc();
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstPragma* nodep, AstNUser*) {
|
||||
if (nodep->pragType() == AstPragmaType::INLINE_MODULE) {
|
||||
//UINFO(0,"PRAG MARK "<<m_modp<<endl);
|
||||
if (!m_modp) {
|
||||
nodep->v3error("Inline pragma not under a module");
|
||||
} else {
|
||||
m_modp->user1(1);
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL; // Remove so don't propagate to upper cell...
|
||||
} else if (nodep->pragType() == AstPragmaType::NO_INLINE_MODULE) {
|
||||
if (!m_modp) {
|
||||
nodep->v3error("Inline pragma not under a module");
|
||||
} else {
|
||||
cantInline("Pragma NO_INLINE_MODULE",false);
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL; // Remove so don't propagate to upper cell...
|
||||
} else {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep, AstNUser*) {
|
||||
// Cleanup link until V3LinkDot can correct it
|
||||
nodep->varp(NULL);
|
||||
}
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
// Can't look at AstIfaceRefDType directly as it is no longer underneath the module
|
||||
if (nodep->isIfaceRef()) {
|
||||
// Unsupported: Inlining of modules with ifaces (see AstIface comment above)
|
||||
if (m_modp) cantInline("Interfaced",true);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
|
||||
// Cleanup link until V3LinkDot can correct it
|
||||
if (!nodep->packagep()) nodep->taskp(NULL);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
// Nop's to speed up the loop
|
||||
virtual void visit(AstAlways* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
m_stmtCnt++;
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
// Don't count assignments, as they'll likely flatten out
|
||||
// Still need to iterate though to nullify VarXRefs
|
||||
int oldcnt = m_stmtCnt;
|
||||
nodep->iterateChildren(*this);
|
||||
m_stmtCnt = oldcnt;
|
||||
}
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
m_stmtCnt++;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
InlineMarkVisitor(AstNode* nodep) {
|
||||
m_modp = NULL;
|
||||
m_stmtCnt = 0;
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~InlineMarkVisitor() {
|
||||
V3Stats::addStat("Optimizations, Inline unsupported", m_statUnsup);
|
||||
// Done with these, are not outputs
|
||||
AstNode::user2ClearTree();
|
||||
AstNode::user3ClearTree();
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Using clonep(), find cell cross references.
|
||||
// clone() must not be called inside this visitor
|
||||
|
||||
class InlineCollectVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Output:
|
||||
// AstCell::user4p() // AstCell* of the created clone
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstCell* nodep, AstNUser*) {
|
||||
nodep->user4p(nodep->clonep());
|
||||
}
|
||||
// Accelerate
|
||||
virtual void visit(AstNodeStmt* nodep, AstNUser*) {}
|
||||
virtual void visit(AstNodeMath* nodep, AstNUser*) {}
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
InlineCollectVisitor(AstNodeModule* nodep) { // passed OLD module, not new one
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~InlineCollectVisitor() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// After cell is cloned, relink the new module's contents
|
||||
|
||||
class InlineRelinkVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Input:
|
||||
// See InlineVisitor
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCell* m_cellp; // Cell being cloned
|
||||
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstCellInline* nodep, AstNUser*) {
|
||||
// Inlined cell under the inline cell, need to move to avoid conflicts
|
||||
nodep->unlinkFrBack();
|
||||
m_modp->addInlinesp(nodep);
|
||||
// Rename
|
||||
string name = m_cellp->name() + "__DOT__" + nodep->name();
|
||||
nodep->name(name);
|
||||
UINFO(6, " Inline "<<nodep<<endl);
|
||||
// Do CellInlines under this, but don't move them
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstCell* nodep, AstNUser*) {
|
||||
// Cell under the inline cell, need to rename to avoid conflicts
|
||||
string name = m_cellp->name() + "__DOT__" + nodep->name();
|
||||
nodep->name(name);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
if (nodep->user2p()) {
|
||||
// Make an assignment, so we'll trace it properly
|
||||
// user2p is either a const or a var.
|
||||
AstConst* exprconstp = nodep->user2p()->castNode()->castConst();
|
||||
AstVarRef* exprvarrefp = nodep->user2p()->castNode()->castVarRef();
|
||||
UINFO(8,"connectto: "<<nodep->user2p()->castNode()<<endl);
|
||||
if (!exprconstp && !exprvarrefp) {
|
||||
nodep->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up\n");
|
||||
}
|
||||
if (exprconstp) {
|
||||
m_modp->addStmtp(new AstAssignW(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep, true),
|
||||
exprconstp->cloneTree(true)));
|
||||
} else if (nodep->user3()) {
|
||||
// Public variable at the lower module end - we need to make sure we propagate
|
||||
// the logic changes up and down; if we aliased, we might remove the change detection
|
||||
// on the output variable.
|
||||
UINFO(9,"public pin assign: "<<exprvarrefp<<endl);
|
||||
if (nodep->isInput()) nodep->v3fatalSrc("Outputs only - inputs use AssignAlias");
|
||||
m_modp->addStmtp(new AstAssignW(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), exprvarrefp->varp(), true),
|
||||
new AstVarRef(nodep->fileline(), nodep, false)));
|
||||
} else if (nodep->isIfaceRef()) {
|
||||
m_modp->addStmtp(new AstAssignVarScope(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep, true),
|
||||
new AstVarRef(nodep->fileline(), exprvarrefp->varp(), false)));
|
||||
AstNode* nodebp=exprvarrefp->varp();
|
||||
nodep ->fileline()->modifyStateInherit(nodebp->fileline());
|
||||
nodebp->fileline()->modifyStateInherit(nodep ->fileline());
|
||||
} else {
|
||||
// Do to inlining child's variable now within the same module, so a AstVarRef not AstVarXRef below
|
||||
m_modp->addStmtp(new AstAssignAlias(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep, true),
|
||||
new AstVarRef(nodep->fileline(), exprvarrefp->varp(), false)));
|
||||
AstNode* nodebp=exprvarrefp->varp();
|
||||
nodep ->fileline()->modifyStateInherit(nodebp->fileline());
|
||||
nodebp->fileline()->modifyStateInherit(nodep ->fileline());
|
||||
}
|
||||
}
|
||||
// Variable under the inline cell, need to rename to avoid conflicts
|
||||
// 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 (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
|
||||
if (AstIfaceRefDType* ifacerefp = nodep->dtypep()->castIfaceRefDType()) {
|
||||
// Relink to point to newly cloned cell
|
||||
if (ifacerefp->cellp()) {
|
||||
if (AstCell* newcellp = ifacerefp->cellp()->user4p()->castNode()->castCell()) {
|
||||
ifacerefp->cellp(newcellp);
|
||||
ifacerefp->cellName(newcellp->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
||||
// Function under the inline cell, need to rename to avoid conflicts
|
||||
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep, AstNUser*) {
|
||||
// Typedef under the inline cell, need to rename to avoid conflicts
|
||||
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
if (nodep->varp()->user2p() // It's being converted to an alias.
|
||||
&& !nodep->varp()->user3()
|
||||
&& !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases (we just made)
|
||||
AstConst* exprconstp = nodep->varp()->user2p()->castNode()->castConst();
|
||||
AstVarRef* exprvarrefp = nodep->varp()->user2p()->castNode()->castVarRef();
|
||||
if (exprconstp) {
|
||||
nodep->replaceWith(exprconstp->cloneTree(true));
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
return;
|
||||
}
|
||||
else if (exprvarrefp) {
|
||||
nodep->varp( exprvarrefp->varp() );
|
||||
}
|
||||
else {
|
||||
nodep->v3fatalSrc("Null connection?\n");
|
||||
}
|
||||
}
|
||||
nodep->name(nodep->varp()->name());
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep, AstNUser*) {
|
||||
// Track what scope it was originally under so V3LinkDot can resolve it
|
||||
string newname = m_cellp->name();
|
||||
if (nodep->inlinedDots() != "") { newname += "." + nodep->inlinedDots(); }
|
||||
nodep->inlinedDots(newname);
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
|
||||
// Track what scope it was originally under so V3LinkDot can resolve it
|
||||
string newname = m_cellp->name();
|
||||
if (nodep->inlinedDots() != "") { newname += "." + nodep->inlinedDots(); }
|
||||
nodep->inlinedDots(newname);
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
// Not needed, as V3LinkDot doesn't care about typedefs
|
||||
//virtual void visit(AstRefDType* nodep, AstNUser*) {}
|
||||
|
||||
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()
|
||||
// Similar code in V3Begin
|
||||
// To keep correct visual order, must add before other Text's
|
||||
AstNode* afterp = nodep->scopeAttrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_cellp->name()));
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstCoverDecl* nodep, AstNUser*) {
|
||||
// Fix path in coverage statements
|
||||
nodep->hier(m_cellp->prettyName()
|
||||
+ (nodep->hier()!="" ? ".":"")
|
||||
+ nodep->hier());
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
InlineRelinkVisitor(AstNodeModule* cloneModp, AstNodeModule* oldModp, AstCell* cellp) {
|
||||
m_modp = oldModp;
|
||||
m_cellp = cellp;
|
||||
cloneModp->accept(*this);
|
||||
}
|
||||
virtual ~InlineRelinkVisitor() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Inline state, as a visitor of each AstNode
|
||||
|
||||
class InlineVisitor : public AstNVisitor {
|
||||
private:
|
||||
@@ -56,11 +421,13 @@ private:
|
||||
// AstNodeModule::user1p() // bool. True to inline this module (from InlineMarkVisitor)
|
||||
// Cleared each cell
|
||||
// AstVar::user2p() // AstVarRef*/AstConst* Points to signal this is a direct connect to
|
||||
// AstVar::user3() // bool Don't alias the user4, keep it as signal
|
||||
// AstVar::user3() // bool Don't alias the user2, keep it as signal
|
||||
// AstCell::user4 // AstCell* of the created clone
|
||||
|
||||
AstUser4InUse m_inuser4;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCell* m_cellp; // Cell being cloned
|
||||
V3Double0 m_statCells; // Statistic tracking
|
||||
|
||||
static int debug() {
|
||||
@@ -75,34 +442,11 @@ private:
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||
if (m_cellp) {
|
||||
} else {
|
||||
m_modp = nodep;
|
||||
}
|
||||
m_modp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstCellInline* nodep, AstNUser*) {
|
||||
// Inlined cell under the inline cell, need to move to avoid conflicts
|
||||
if (m_cellp) {
|
||||
nodep->unlinkFrBack();
|
||||
m_modp->addInlinesp(nodep);
|
||||
// Rename
|
||||
string name = m_cellp->name() + "__DOT__" + nodep->name();
|
||||
nodep->name(name);
|
||||
UINFO(6, " Inline "<<nodep<<endl);
|
||||
// Do CellInlines under this, but don't move them
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep, AstNUser*) {
|
||||
if (m_cellp) {
|
||||
// Cell under the inline cell, need to rename to avoid conflicts
|
||||
string name = m_cellp->name() + "__DOT__" + nodep->name();
|
||||
nodep->name(name);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
if (nodep->modp()->user1()) { // Marked with inline request
|
||||
if (m_cellp) nodep->v3error("Cloning should have already been done bottom-up");
|
||||
UINFO(5," Inline CELL "<<nodep<<endl);
|
||||
UINFO(5," To MOD "<<m_modp<<endl);
|
||||
++m_statCells;
|
||||
@@ -121,8 +465,10 @@ private:
|
||||
//if (debug()>=9) { nodep->modp()->dumpTree(cout,"oldmod:"); }
|
||||
AstNodeModule* newmodp = nodep->modp()->cloneTree(false);
|
||||
if (debug()>=9) { newmodp->dumpTree(cout,"newmod:"); }
|
||||
// Clear var markings
|
||||
// Clear var markings and find cell cross references
|
||||
AstNode::user2ClearTree();
|
||||
AstNode::user4ClearTree();
|
||||
{ InlineCollectVisitor(nodep->modp()); } // {} to destroy visitor immediately
|
||||
// Create data for dotted variable resolution
|
||||
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
|
||||
nodep->name(), nodep->modp()->origName());
|
||||
@@ -163,9 +509,7 @@ private:
|
||||
pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() && pinNewVarp->isOutOnly());
|
||||
}
|
||||
// Cleanup var names, etc, to not conflict
|
||||
m_cellp = nodep;
|
||||
newmodp->iterate(*this); // Not iterateAndNext because newmodp isn't linked; no back
|
||||
m_cellp = NULL;
|
||||
{ InlineRelinkVisitor(newmodp, m_modp, nodep); }
|
||||
// Move statements to top module
|
||||
if (debug()>=9) { newmodp->dumpTree(cout,"fixmod:"); }
|
||||
AstNode* stmtsp = newmodp->stmtsp();
|
||||
@@ -178,133 +522,10 @@ private:
|
||||
if (debug()>=9) { m_modp->dumpTree(cout,"donemod:"); }
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
if (m_cellp) {
|
||||
if (nodep->user2p()) {
|
||||
// Make an assignment, so we'll trace it properly
|
||||
// user2p is either a const or a var.
|
||||
AstConst* exprconstp = nodep->user2p()->castNode()->castConst();
|
||||
AstVarRef* exprvarrefp = nodep->user2p()->castNode()->castVarRef();
|
||||
UINFO(8,"connectto: "<<nodep->user2p()->castNode()<<endl);
|
||||
if (!exprconstp && !exprvarrefp) {
|
||||
nodep->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up\n");
|
||||
}
|
||||
if (exprconstp) {
|
||||
m_modp->addStmtp(new AstAssignW(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep, true),
|
||||
exprconstp->cloneTree(true)));
|
||||
} else if (nodep->user3()) {
|
||||
// Public variable at the lower module end - we need to make sure we propagate
|
||||
// the logic changes up and down; if we aliased, we might remove the change detection
|
||||
// on the output variable.
|
||||
UINFO(9,"public pin assign: "<<exprvarrefp<<endl);
|
||||
if (nodep->isInput()) nodep->v3fatalSrc("Outputs only - inputs use AssignAlias");
|
||||
m_modp->addStmtp(new AstAssignW(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), exprvarrefp->varp(), true),
|
||||
new AstVarRef(nodep->fileline(), nodep, false)));
|
||||
} else {
|
||||
m_modp->addStmtp(new AstAssignAlias(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep, true),
|
||||
new AstVarRef(nodep->fileline(), exprvarrefp->varp(), false)));
|
||||
AstNode* nodebp=exprvarrefp->varp();
|
||||
nodep ->fileline()->modifyStateInherit(nodebp->fileline());
|
||||
nodebp->fileline()->modifyStateInherit(nodep ->fileline());
|
||||
}
|
||||
}
|
||||
// Variable under the inline cell, need to rename to avoid conflicts
|
||||
// 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 (debug()>=9) { nodep->dumpTree(cout,"varchanged:"); }
|
||||
if (debug()>=9) { nodep->valuep()->dumpTree(cout,"varchangei:"); }
|
||||
}
|
||||
if (nodep) nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
||||
if (m_cellp) {
|
||||
// Function under the inline cell, need to rename to avoid conflicts
|
||||
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep, AstNUser*) {
|
||||
if (m_cellp) {
|
||||
// Typedef under the inline cell, need to rename to avoid conflicts
|
||||
nodep->name(m_cellp->name() + "__DOT__" + nodep->name());
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
if (m_cellp) {
|
||||
if (nodep->varp()->user2p() // It's being converted to an alias.
|
||||
&& !nodep->varp()->user3()
|
||||
&& !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases (we just made)
|
||||
AstConst* exprconstp = nodep->varp()->user2p()->castNode()->castConst();
|
||||
AstVarRef* exprvarrefp = nodep->varp()->user2p()->castNode()->castVarRef();
|
||||
if (exprconstp) {
|
||||
nodep->replaceWith(exprconstp->cloneTree(true));
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
return;
|
||||
}
|
||||
else if (exprvarrefp) {
|
||||
nodep->varp( exprvarrefp->varp() );
|
||||
}
|
||||
else {
|
||||
nodep->v3fatalSrc("Null connection?\n");
|
||||
}
|
||||
}
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep, AstNUser*) {
|
||||
if (m_cellp) {
|
||||
// Track what scope it was originally under so V3LinkDot can resolve it
|
||||
string newname = m_cellp->name();
|
||||
if (nodep->inlinedDots() != "") { newname += "." + nodep->inlinedDots(); }
|
||||
nodep->inlinedDots(newname);
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
|
||||
if (m_cellp) {
|
||||
// Track what scope it was originally under so V3LinkDot can resolve it
|
||||
string newname = m_cellp->name();
|
||||
if (nodep->inlinedDots() != "") { newname += "." + nodep->inlinedDots(); }
|
||||
nodep->inlinedDots(newname);
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
// Not needed, as V3LinkDot doesn't care about typedefs
|
||||
//virtual void visit(AstRefDType* nodep, AstNUser*) {}
|
||||
|
||||
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()
|
||||
// Similar code in V3Begin
|
||||
if (m_cellp) {
|
||||
// To keep correct visual order, must add before other Text's
|
||||
AstNode* afterp = nodep->scopeAttrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_cellp->name()));
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstCoverDecl* nodep, AstNUser*) {
|
||||
// Fix path in coverage statements
|
||||
if (m_cellp) {
|
||||
nodep->hier(m_cellp->prettyName()
|
||||
+ (nodep->hier()!="" ? ".":"")
|
||||
+ nodep->hier());
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNodeMath* nodep, AstNUser*) {} // Accelerate
|
||||
virtual void visit(AstNodeStmt* nodep, AstNUser*) {} // Accelerate
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
@@ -312,7 +533,6 @@ private:
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
InlineVisitor(AstNode* nodep) {
|
||||
m_cellp = NULL;
|
||||
m_modp = NULL;
|
||||
nodep->accept(*this);
|
||||
}
|
||||
@@ -321,126 +541,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Inline state, as a visitor of each AstNode
|
||||
|
||||
class InlineMarkVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist
|
||||
// AstNodeModule::user1() // OUTPUT: bool. User request to inline this module
|
||||
// AstNodeModule::user2() // bool. Allowed to automatically inline module
|
||||
// AstNodeModule::user3() // int. Number of cells referencing this module
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
int m_stmtCnt; // Statements in module
|
||||
|
||||
// METHODS
|
||||
void cantInline(const char* reason) {
|
||||
if (m_modp->user2()) {
|
||||
UINFO(4," No inline: "<<reason<<" "<<m_modp<<endl);
|
||||
m_modp->user2(false);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
||||
m_stmtCnt = 0;
|
||||
m_modp = nodep;
|
||||
m_modp->user2(true); // Allowed = true
|
||||
if (m_modp->modPublic()) cantInline("modPublic");
|
||||
//
|
||||
nodep->iterateChildren(*this);
|
||||
//
|
||||
bool userinline = nodep->user1();
|
||||
bool allowed = nodep->user2();
|
||||
int refs = nodep->user3();
|
||||
// Should we automatically inline this module?
|
||||
// inlineMult = 2000 by default. If a mod*#instances is < this # nodes, can inline it
|
||||
bool doit = (userinline || (allowed && (refs==1
|
||||
|| m_stmtCnt < INLINE_MODS_SMALLER
|
||||
|| v3Global.opt.inlineMult() < 1
|
||||
|| refs*m_stmtCnt < v3Global.opt.inlineMult())));
|
||||
// Packages aren't really "under" anything so they confuse this algorithm
|
||||
if (nodep->castPackage()) doit = false;
|
||||
UINFO(4, " Inline="<<doit<<" Possible="<<allowed<<" Usr="<<userinline<<" Refs="<<refs<<" Stmts="<<m_stmtCnt
|
||||
<<" "<<nodep<<endl);
|
||||
if (doit) {
|
||||
UINFO(4," AutoInline "<<nodep<<endl);
|
||||
nodep->user1(true);
|
||||
}
|
||||
m_modp = NULL;
|
||||
}
|
||||
virtual void visit(AstCell* nodep, AstNUser*) {
|
||||
nodep->modp()->user3Inc();
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstPragma* nodep, AstNUser*) {
|
||||
if (nodep->pragType() == AstPragmaType::INLINE_MODULE) {
|
||||
//UINFO(0,"PRAG MARK "<<m_modp<<endl);
|
||||
if (!m_modp) {
|
||||
nodep->v3error("Inline pragma not under a module");
|
||||
} else {
|
||||
m_modp->user1(1);
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL; // Remove so don't propagate to upper cell...
|
||||
} else if (nodep->pragType() == AstPragmaType::NO_INLINE_MODULE) {
|
||||
if (!m_modp) {
|
||||
nodep->v3error("Inline pragma not under a module");
|
||||
} else {
|
||||
cantInline("Pragma NO_INLINE_MODULE");
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL; // Remove so don't propagate to upper cell...
|
||||
} else {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep, AstNUser*) {
|
||||
// Cleanup link until V3LinkDot can correct it
|
||||
nodep->varp(NULL);
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
|
||||
// Cleanup link until V3LinkDot can correct it
|
||||
if (!nodep->packagep()) nodep->taskp(NULL);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
// Nop's to speed up the loop
|
||||
virtual void visit(AstAlways* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
m_stmtCnt++;
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
// Don't count assignments, as they'll likely flatten out
|
||||
// Still need to iterate though to nullify VarXRefs
|
||||
int oldcnt = m_stmtCnt;
|
||||
nodep->iterateChildren(*this);
|
||||
m_stmtCnt = oldcnt;
|
||||
}
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
m_stmtCnt++;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
InlineMarkVisitor(AstNode* nodep) {
|
||||
m_modp = NULL;
|
||||
m_stmtCnt = 0;
|
||||
//VV***** We reset all userp() on the whole netlist!!!
|
||||
AstNode::user1ClearTree();
|
||||
AstNode::user2ClearTree();
|
||||
AstNode::user3ClearTree();
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~InlineMarkVisitor() {}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Inline class functions
|
||||
|
||||
|
||||
+18
-6
@@ -105,6 +105,13 @@ private:
|
||||
exprp);
|
||||
m_modp->addStmtp(assp);
|
||||
if (debug()>=9) assp->dumpTree(cout," _new: ");
|
||||
} else if (nodep->modVarp()->isIfaceRef()) {
|
||||
// Create an AstAssignVarScope for Vars to Cells so we can link with their scope later
|
||||
AstNode* lhsp = new AstVarXRef (exprp->fileline(), nodep->modVarp(), m_cellp->name(), false);
|
||||
AstVarRef* refp = exprp->castVarRef();
|
||||
if (!refp) exprp->v3fatalSrc("Interfaces: Pin is not connected to a VarRef");
|
||||
AstAssignVarScope* assp = new AstAssignVarScope(exprp->fileline(), lhsp, refp);
|
||||
m_modp->addStmtp(assp);
|
||||
} else {
|
||||
nodep->v3error("Assigned pin is neither input nor output");
|
||||
}
|
||||
@@ -252,7 +259,11 @@ AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModu
|
||||
&& connectRefp
|
||||
&& connectRefp->varp()->dtypep()->sameTree(pinVarp->dtypep())
|
||||
&& !connectRefp->varp()->isSc()) { // Need the signal as a 'shell' to convert types
|
||||
// Done. Same data type
|
||||
// Done. Same data type
|
||||
} else if (!alwaysCvt
|
||||
&& connectRefp
|
||||
&& connectRefp->varp()->isIfaceRef()) {
|
||||
// Done. Interface
|
||||
} else if (!alwaysCvt
|
||||
&& connBasicp
|
||||
&& pinBasicp
|
||||
@@ -266,10 +277,11 @@ AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModu
|
||||
// Done. Constant.
|
||||
} else {
|
||||
// Make a new temp wire
|
||||
//if (1||debug()>=9) { pinp->dumpTree(cout,"in_pin:"); }
|
||||
//if (1||debug()>=9) { pinp->dumpTree(cout,"-in_pin:"); }
|
||||
AstNode* pinexprp = pinp->exprp()->unlinkFrBack();
|
||||
string newvarname = ((pinVarp->isOutput() ? "__Vcellout__" : "__Vcellinp__")
|
||||
+cellp->name()+"__"+pinp->name());
|
||||
string newvarname = ((string)(pinVarp->isOutput() ? "__Vcellout" : "__Vcellinp")
|
||||
+(forTristate?"t":"") // Prevent name conflict if both tri & non-tri add signals
|
||||
+"__"+cellp->name()+"__"+pinp->name());
|
||||
AstVar* newvarp = new AstVar (pinVarp->fileline(), AstVarType::MODULETEMP, newvarname, pinVarp);
|
||||
// Important to add statement next to cell, in case there is a generate with same named cell
|
||||
cellp->addNextHere(newvarp);
|
||||
@@ -298,8 +310,8 @@ AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModu
|
||||
pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, false));
|
||||
}
|
||||
if (assignp) cellp->addNextHere(assignp);
|
||||
//if (1||debug()) { pinp->dumpTree(cout," out:"); }
|
||||
//if (1||debug()) { assignp->dumpTree(cout," aout:"); }
|
||||
//if (debug()) { pinp->dumpTree(cout,"- out:"); }
|
||||
//if (debug()) { assignp->dumpTree(cout,"- aout:"); }
|
||||
}
|
||||
return assignp;
|
||||
}
|
||||
|
||||
+5
-3
@@ -41,6 +41,7 @@ public:
|
||||
L1364_2005,
|
||||
L1800_2005,
|
||||
L1800_2009,
|
||||
L1800_2012,
|
||||
// ***Add new elements below also***
|
||||
_ENUM_END
|
||||
};
|
||||
@@ -52,12 +53,13 @@ public:
|
||||
"1364-2001",
|
||||
"1364-2005",
|
||||
"1800-2005",
|
||||
"1800-2009"
|
||||
"1800-2009",
|
||||
"1800-2012"
|
||||
};
|
||||
return names[m_e];
|
||||
};
|
||||
static V3LangCode mostRecent() { return V3LangCode(L1800_2009); }
|
||||
bool systemVerilog() const { return m_e == L1800_2005 || m_e == L1800_2009; }
|
||||
static V3LangCode mostRecent() { return V3LangCode(L1800_2012); }
|
||||
bool systemVerilog() const { return m_e == L1800_2005 || m_e == L1800_2009 || m_e == L1800_2012; }
|
||||
bool legal() const { return m_e != L_ERROR; }
|
||||
//
|
||||
enum en m_e;
|
||||
|
||||
@@ -37,9 +37,9 @@ class V3LanguageWords {
|
||||
}
|
||||
public:
|
||||
string isKeyword(const string& kwd) {
|
||||
map<string,string>::iterator iter = m_kwdMap.find(kwd);
|
||||
if (iter == m_kwdMap.end()) return "";
|
||||
return iter->second;
|
||||
map<string,string>::iterator it = m_kwdMap.find(kwd);
|
||||
if (it == m_kwdMap.end()) return "";
|
||||
return it->second;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
+43
-4
@@ -75,7 +75,7 @@ public:
|
||||
void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) {
|
||||
if (LinkCellsVertex* vvertexp = dynamic_cast<LinkCellsVertex*>(vertexp)) {
|
||||
vvertexp->modp()->v3error("Recursive module (module instantiates itself): "
|
||||
<<vvertexp->modp()->name());
|
||||
<<vvertexp->modp()->prettyName());
|
||||
V3Error::abortIfErrors();
|
||||
} else { // Everything should match above, but...
|
||||
v3fatalSrc("Recursive instantiations");
|
||||
@@ -128,15 +128,17 @@ private:
|
||||
// Read-subfile
|
||||
// If file not found, make AstNotFoundModule, rather than error out.
|
||||
// We'll throw the error when we know the module will really be needed.
|
||||
string prettyName = AstNode::prettyName(modName);
|
||||
V3Parse parser (v3Global.rootp(), m_filterp, m_parseSymp);
|
||||
parser.parseFile(nodep->fileline(), modName, false, "");
|
||||
parser.parseFile(nodep->fileline(), prettyName, false, "");
|
||||
V3Error::abortIfErrors();
|
||||
// We've read new modules, grab new pointers to their names
|
||||
readModNames();
|
||||
// Check again
|
||||
modp = m_mods.rootp()->findIdFallback(modName)->nodep()->castNodeModule();
|
||||
if (!modp) {
|
||||
nodep->v3error("Can't resolve module reference: "<<modName);
|
||||
// This shouldn't throw a message as parseFile will create a AstNotFoundModule for us
|
||||
nodep->v3error("Can't resolve module reference: "<<prettyName);
|
||||
}
|
||||
}
|
||||
return modp;
|
||||
@@ -188,7 +190,8 @@ private:
|
||||
<<"' does not match "<<nodep->typeName()<<" name: "<<nodep->prettyName());
|
||||
}
|
||||
}
|
||||
bool topMatch = (v3Global.opt.topModule()==nodep->name());
|
||||
if (nodep->castIface() || nodep->castPackage()) nodep->inLibrary(true); // Interfaces can't be at top, unless asked
|
||||
bool topMatch = (v3Global.opt.topModule()==nodep->prettyName());
|
||||
if (topMatch) {
|
||||
m_topVertexp = vertex(nodep);
|
||||
UINFO(2,"Link --top-module: "<<nodep<<endl);
|
||||
@@ -208,6 +211,25 @@ private:
|
||||
m_modp = NULL;
|
||||
}
|
||||
|
||||
virtual void visit(AstIfaceRefDType* nodep, AstNUser*) {
|
||||
// Cell: Resolve its filename. If necessary, parse it.
|
||||
UINFO(4,"Link IfaceRef: "<<nodep<<endl);
|
||||
// Use findIdUpward instead of findIdFlat; it doesn't matter for now
|
||||
// but we might support modules-under-modules someday.
|
||||
AstNodeModule* modp = resolveModule(nodep, nodep->ifaceName());
|
||||
if (modp) {
|
||||
if (modp->castIface()) {
|
||||
// Track module depths, so can sort list from parent down to children
|
||||
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false);
|
||||
if (!nodep->cellp()) nodep->ifacep(modp->castIface());
|
||||
} else if (modp->castNotFoundModule()) { // Will error out later
|
||||
} else {
|
||||
nodep->v3error("Non-interface used as an interface: "<<nodep->prettyName());
|
||||
}
|
||||
}
|
||||
// Note cannot do modport resolution here; modports are allowed underneath generates
|
||||
}
|
||||
|
||||
virtual void visit(AstPackageImport* nodep, AstNUser*) {
|
||||
// Package Import: We need to do the package before the use of a package
|
||||
nodep->iterateChildren(*this);
|
||||
@@ -312,6 +334,23 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nodep->modp()->castIface()) {
|
||||
// Cell really is the parent's instantiation of an interface, not a normal module
|
||||
// Make sure we have a variable to refer to this cell, so can <ifacename>.<innermember>
|
||||
// in the same way that a child does. Rename though to avoid conflict with cell.
|
||||
// This is quite similar to how classes work; when unpacked classes are better supported
|
||||
// may remap interfaces to be more like a class.
|
||||
if (!nodep->hasIfaceVar()) {
|
||||
string varName = nodep->name()+"__Viftop"; // V3LinkDot looks for this naming
|
||||
AstIfaceRefDType* idtypep = new AstIfaceRefDType(nodep->fileline(), nodep->name(), nodep->modp()->name());
|
||||
idtypep->cellp(nodep); // Only set when real parent cell known
|
||||
idtypep->ifacep(NULL); // cellp overrides
|
||||
AstVar* varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName, VFlagChildDType(), idtypep);
|
||||
varp->isIfaceParent(true);
|
||||
nodep->addNextHere(varp);
|
||||
nodep->hasIfaceVar(true);
|
||||
}
|
||||
}
|
||||
if (nodep->modp()) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
+375
-52
@@ -31,6 +31,26 @@
|
||||
// VarXRef/Func's:
|
||||
// Find appropriate named cell and link to var they reference
|
||||
//*************************************************************************
|
||||
// Interfaces:
|
||||
// CELL (.port (ifref)
|
||||
// ^--- cell -> IfaceDTypeRef(iface)
|
||||
// ^--- cell.modport -> IfaceDTypeRef(iface,modport)
|
||||
// ^--- varref(input_ifref) -> IfaceDTypeRef(iface)
|
||||
// ^--- varref(input_ifref).modport -> IfaceDTypeRef(iface,modport)
|
||||
// FindVisitor:
|
||||
// #1: Insert interface Vars
|
||||
// #2: Insert ModPort names
|
||||
// IfaceVisitor:
|
||||
// #3: Update ModPortVarRef to point at interface vars (after #1)
|
||||
// #4: Create ModPortVarRef symbol table entries
|
||||
// FindVisitor-insertIfaceRefs()
|
||||
// #5: Resolve IfaceRefDtype modport names (after #2)
|
||||
// #7: Record sym of IfaceRefDType and aliased interface and/or modport (after #4,#5)
|
||||
// insertAllScopeAliases():
|
||||
// #8: Insert modport's symbols under IfaceRefDType (after #7)
|
||||
// ResolveVisitor:
|
||||
// #9: Resolve general variables, which may point into the interface or modport (after #8)
|
||||
//*************************************************************************
|
||||
// TOP
|
||||
// {name-of-top-modulename}
|
||||
// a (VSymEnt->AstCell)
|
||||
@@ -77,17 +97,26 @@ private:
|
||||
AstUser4InUse m_inuser4;
|
||||
|
||||
// TYPES
|
||||
typedef std::multimap<string,VSymEnt*> NameScopeMap;
|
||||
typedef set <pair<AstNodeModule*,string> > ImplicitNameSet;
|
||||
typedef multimap<string,VSymEnt*> NameScopeSymMap;
|
||||
typedef map<VSymEnt*,VSymEnt*> ScopeAliasMap;
|
||||
typedef set<pair<AstNodeModule*,string> > ImplicitNameSet;
|
||||
typedef vector<VSymEnt*> IfaceVarSyms;
|
||||
typedef vector<pair<AstIface*,VSymEnt*> > IfaceModSyms;
|
||||
|
||||
static LinkDotState* s_errorThisp; // Last self, for error reporting only
|
||||
|
||||
// MEMBERS
|
||||
VSymGraph m_syms; // Symbol table
|
||||
VSymEnt* m_dunitEntp; // $unit entry
|
||||
NameScopeMap m_nameScopeMap; // Hash of scope referenced by non-pretty textual name
|
||||
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
|
||||
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
|
||||
bool m_forPrearray; // Compress cell__[array] refs
|
||||
bool m_forScopeCreation; // Remove VarXRefs for V3Scope
|
||||
|
||||
public:
|
||||
|
||||
static int debug() {
|
||||
@@ -95,14 +124,27 @@ public:
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
void dump() {
|
||||
if (debug()>=6) m_syms.dumpFilePrefixed("linkdot");
|
||||
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));
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void preErrorDumpHandler() {
|
||||
if (s_errorThisp) s_errorThisp->preErrorDump();
|
||||
}
|
||||
void preErrorDump() {
|
||||
static bool diddump = false;
|
||||
if (!diddump && v3Global.opt.dumpTree()) {
|
||||
diddump = true;
|
||||
m_syms.dumpFilePrefixed("linkdot-preerr");
|
||||
dump("linkdot-preerr",true);
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot-preerr.tree"));
|
||||
}
|
||||
}
|
||||
@@ -115,8 +157,13 @@ public:
|
||||
m_forPrearray = (step == LDS_PARAMED || step==LDS_PRIMARY);
|
||||
m_forScopeCreation = (step == LDS_SCOPED);
|
||||
m_dunitEntp = NULL;
|
||||
s_errorThisp = this;
|
||||
V3Error::errorExitCb(preErrorDumpHandler); // If get error, dump self
|
||||
}
|
||||
~LinkDotState() {
|
||||
V3Error::errorExitCb(NULL);
|
||||
s_errorThisp = NULL;
|
||||
}
|
||||
~LinkDotState() {}
|
||||
|
||||
// ACCESSORS
|
||||
VSymGraph* symsp() { return &m_syms; }
|
||||
@@ -131,6 +178,7 @@ public:
|
||||
else if (nodep->castTask()) return "task";
|
||||
else if (nodep->castFunc()) return "function";
|
||||
else if (nodep->castBegin()) return "block";
|
||||
else if (nodep->castIface()) return "interface";
|
||||
else return nodep->prettyTypeName();
|
||||
}
|
||||
static string ucfirst(const string& text) {
|
||||
@@ -161,7 +209,6 @@ public:
|
||||
// Begin: ... blocks often replicate under genif/genfor, so simply suppress duplicate checks
|
||||
// See t_gen_forif.v for an example.
|
||||
} else {
|
||||
preErrorDump();
|
||||
UINFO(4,"name "<<name<<endl); // Not always same as nodep->name
|
||||
UINFO(4,"Var1 "<<nodep<<endl);
|
||||
UINFO(4,"Var2 "<<fnodep<<endl);
|
||||
@@ -195,7 +242,7 @@ public:
|
||||
nodep->user1p(symp);
|
||||
checkDuplicate(rootEntp(), nodep, nodep->origName());
|
||||
rootEntp()->insert(nodep->origName(),symp);
|
||||
if (forScopeCreation()) m_nameScopeMap.insert(make_pair(scopename, symp));
|
||||
if (forScopeCreation()) m_nameScopeSymMap.insert(make_pair(scopename, symp));
|
||||
return symp;
|
||||
}
|
||||
VSymEnt* insertCell(VSymEnt* abovep, VSymEnt* modSymp,
|
||||
@@ -214,7 +261,7 @@ public:
|
||||
// Duplicates are possible, as until resolve generates might have 2 same cells under an if
|
||||
modSymp->reinsert(nodep->name(), symp);
|
||||
}
|
||||
if (forScopeCreation()) m_nameScopeMap.insert(make_pair(scopename, symp));
|
||||
if (forScopeCreation()) m_nameScopeSymMap.insert(make_pair(scopename, symp));
|
||||
return symp;
|
||||
}
|
||||
VSymEnt* insertInline(VSymEnt* abovep, VSymEnt* modSymp,
|
||||
@@ -246,7 +293,7 @@ public:
|
||||
UINFO(9," INSERTblk se"<<(void*)symp<<" above=se"<<(void*)abovep<<" node="<<nodep<<endl);
|
||||
symp->parentp(abovep);
|
||||
symp->packagep(packagep);
|
||||
symp->fallbackp(abovep); // Needed so can find $unit stuff
|
||||
symp->fallbackp(abovep);
|
||||
nodep->user1p(symp);
|
||||
if (name != "") {
|
||||
checkDuplicate(abovep, nodep, name);
|
||||
@@ -278,17 +325,17 @@ public:
|
||||
return symp;
|
||||
}
|
||||
VSymEnt* getScopeSym(AstScope* nodep) {
|
||||
NameScopeMap::iterator iter = m_nameScopeMap.find(nodep->name());
|
||||
if (iter == m_nameScopeMap.end()) {
|
||||
NameScopeSymMap::iterator it = m_nameScopeSymMap.find(nodep->name());
|
||||
if (it == m_nameScopeSymMap.end()) {
|
||||
nodep->v3fatalSrc("Scope never assigned a symbol entry?");
|
||||
}
|
||||
return iter->second;
|
||||
return it->second;
|
||||
}
|
||||
void implicitOkAdd(AstNodeModule* nodep, const string& varname) {
|
||||
// Mark the given variable name as being allowed to be implicitly declared
|
||||
if (nodep) {
|
||||
ImplicitNameSet::iterator iter = m_implicitNameSet.find(make_pair(nodep,varname));
|
||||
if (iter == m_implicitNameSet.end()) {
|
||||
ImplicitNameSet::iterator it = m_implicitNameSet.find(make_pair(nodep,varname));
|
||||
if (it == m_implicitNameSet.end()) {
|
||||
m_implicitNameSet.insert(make_pair(nodep,varname));
|
||||
}
|
||||
}
|
||||
@@ -298,6 +345,69 @@ public:
|
||||
&& (m_implicitNameSet.find(make_pair(nodep,varname)) != m_implicitNameSet.end());
|
||||
}
|
||||
|
||||
// Track and later recurse interface modules
|
||||
void insertIfaceModSym(AstIface* nodep, VSymEnt* symp) {
|
||||
m_ifaceModSyms.push_back(make_pair(nodep, symp));
|
||||
}
|
||||
void computeIfaceModSyms();
|
||||
|
||||
// Track and later insert interface references
|
||||
void insertIfaceVarSym(VSymEnt* symp) { // Where sym is for a VAR of dtype IFACEREFDTYPE
|
||||
m_ifaceVarSyms.push_back(symp);
|
||||
}
|
||||
void computeIfaceVarSyms() {
|
||||
for (IfaceVarSyms::iterator it = m_ifaceVarSyms.begin(); it != m_ifaceVarSyms.end(); ++it) {
|
||||
VSymEnt* varSymp = *it;
|
||||
AstVar* varp = varSymp->nodep()->castVar();
|
||||
UINFO(9, " insAllIface se"<<(void*)varSymp<<" "<<varp<<endl);
|
||||
AstIfaceRefDType* ifacerefp = varp->subDTypep()->castIfaceRefDType();
|
||||
if (!ifacerefp) varp->v3fatalSrc("Non-ifacerefs on list!");
|
||||
if (!ifacerefp->ifaceViaCellp()) ifacerefp->v3fatalSrc("Unlinked interface");
|
||||
VSymEnt* ifaceSymp = getNodeSym(ifacerefp->ifaceViaCellp());
|
||||
VSymEnt* ifOrPortSymp = ifaceSymp;
|
||||
// Link Modport names to the Modport Node under the Interface
|
||||
if (ifacerefp->isModport()) {
|
||||
VSymEnt* foundp = ifaceSymp->findIdFallback(ifacerefp->modportName());
|
||||
bool ok = false;
|
||||
if (foundp) {
|
||||
if (AstModport* modportp = foundp->nodep()->castModport()) {
|
||||
UINFO(4,"Link Modport: "<<modportp<<endl);
|
||||
ifacerefp->modportp(modportp);
|
||||
ifOrPortSymp = foundp;
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
if (!ok) ifacerefp->v3error("Modport not found under interface '"
|
||||
<<ifacerefp->prettyName(ifacerefp->ifaceName())
|
||||
<<"': "<<ifacerefp->prettyName(ifacerefp->modportName()));
|
||||
}
|
||||
// Alias won't expand until interfaces and modport names are known; see notes at top
|
||||
insertScopeAlias(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
|
||||
UINFO(9," insertScopeAlias se"<<(void*)lhsp<<" se"<<(void*)rhsp<<endl);
|
||||
m_scopeAliasMap.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;
|
||||
}
|
||||
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.clear();
|
||||
}
|
||||
private:
|
||||
VSymEnt* findWithAltFallback(VSymEnt* symp, const string& name, const string& altname) {
|
||||
VSymEnt* findp = symp->findIdFallback(name);
|
||||
@@ -399,10 +509,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
LinkDotState* LinkDotState::s_errorThisp = NULL;
|
||||
|
||||
//======================================================================
|
||||
|
||||
class LinkDotFindVisitor : public AstNVisitor {
|
||||
private:
|
||||
// STATE
|
||||
LinkDotState* m_statep; // State to pass between visitors, including symbol table
|
||||
AstPackage* m_packagep; // Current package
|
||||
@@ -486,6 +597,10 @@ private:
|
||||
// Iterate
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->user4(true);
|
||||
// Interfaces need another pass when signals are resolved
|
||||
if (AstIface* ifacep = nodep->castIface()) {
|
||||
m_statep->insertIfaceModSym(ifacep, m_curSymp);
|
||||
}
|
||||
} else { //!doit
|
||||
// Will be optimized away later
|
||||
// Can't remove now, as our backwards iterator will throw up
|
||||
@@ -525,7 +640,6 @@ private:
|
||||
VSymEnt* okSymp;
|
||||
aboveSymp = m_statep->findDotted(aboveSymp, scope, baddot, okSymp);
|
||||
if (!aboveSymp) {
|
||||
m_statep->preErrorDump();
|
||||
nodep->v3fatalSrc("Can't find cell insertion point at '"<<baddot<<"' in: "<<nodep->prettyName());
|
||||
}
|
||||
}
|
||||
@@ -554,7 +668,6 @@ private:
|
||||
VSymEnt* okSymp;
|
||||
aboveSymp = m_statep->findDotted(aboveSymp, dotted, baddot, okSymp);
|
||||
if (!aboveSymp) {
|
||||
m_statep->preErrorDump();
|
||||
nodep->v3fatalSrc("Can't find cellinline insertion point at '"<<baddot<<"' in: "<<nodep->prettyName());
|
||||
}
|
||||
m_statep->insertInline(aboveSymp, m_modSymp, nodep, ident);
|
||||
@@ -666,7 +779,6 @@ private:
|
||||
if (!foundp) {
|
||||
ins=true;
|
||||
} else if (!findvarp && foundp && m_curSymp->findIdFlat(nodep->name())) {
|
||||
m_statep->preErrorDump();
|
||||
nodep->v3error("Unsupported in C: Variable has same name as "
|
||||
<<LinkDotState::nodeTextType(foundp->nodep())<<": "<<nodep->prettyName());
|
||||
} else if (findvarp != nodep) {
|
||||
@@ -698,19 +810,23 @@ private:
|
||||
&& (!m_ftaskp || m_ftaskp != foundp->nodep()) // Not the function's variable hiding function
|
||||
&& !nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)
|
||||
&& !foundp->nodep()->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) {
|
||||
nodep->v3warn(VARHIDDEN,"Declaration of signal hides declaration in upper scope: "<<nodep->name()<<endl
|
||||
nodep->v3warn(VARHIDDEN,"Declaration of signal hides declaration in upper scope: "<<nodep->prettyName()<<endl
|
||||
<<foundp->nodep()->warnMore()<<"... Location of original declaration");
|
||||
}
|
||||
ins = true;
|
||||
}
|
||||
}
|
||||
if (ins) {
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
|
||||
VSymEnt* insp = m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
|
||||
if (m_statep->forPrimary() && nodep->isGParam()) {
|
||||
m_paramNum++;
|
||||
VSymEnt* symp = m_statep->insertSym(m_curSymp, "__paramNumber"+cvtToStr(m_paramNum), nodep, m_packagep);
|
||||
symp->exported(false);
|
||||
}
|
||||
if (nodep->subDTypep()->castIfaceRefDType()) {
|
||||
// Can't resolve until interfaces and modport names are known; see notes at top
|
||||
m_statep->insertIfaceVarSym(insp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -744,7 +860,7 @@ private:
|
||||
// User can disable the message at either point
|
||||
if (!nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)
|
||||
&& !foundp->nodep()->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) {
|
||||
nodep->v3warn(VARHIDDEN,"Declaration of enum value hides declaration in upper scope: "<<nodep->name()<<endl
|
||||
nodep->v3warn(VARHIDDEN,"Declaration of enum value hides declaration in upper scope: "<<nodep->prettyName()<<endl
|
||||
<<foundp->nodep()->warnMore()<<"... Location of original declaration");
|
||||
}
|
||||
ins = true;
|
||||
@@ -763,7 +879,7 @@ private:
|
||||
nodep->v3error("Import object not found: "<<nodep->packagep()->prettyName()<<"::"<<nodep->prettyName());
|
||||
}
|
||||
}
|
||||
m_curSymp->import(m_statep->symsp(), srcp, nodep->name());
|
||||
m_curSymp->importFromPackage(m_statep->symsp(), srcp, nodep->name());
|
||||
UINFO(9," Link Done: "<<nodep<<endl);
|
||||
// No longer needed, but can't delete until any multi-instantiated modules are expanded
|
||||
}
|
||||
@@ -858,7 +974,7 @@ private:
|
||||
}
|
||||
virtual void visit(AstDefParam* nodep, AstNUser*) {
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->v3warn(DEFPARAM,"Suggest replace defparam with Verilog 2001 #(."<<nodep->name()<<"(...etc...))");
|
||||
nodep->v3warn(DEFPARAM,"Suggest replace defparam with Verilog 2001 #(."<<nodep->prettyName()<<"(...etc...))");
|
||||
VSymEnt* foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->path());
|
||||
AstCell* cellp = foundp->nodep()->castCell();
|
||||
if (!cellp) {
|
||||
@@ -885,8 +1001,8 @@ private:
|
||||
AstVar* refp = foundp->nodep()->castVar();
|
||||
if (!refp) {
|
||||
nodep->v3error("Input/output/inout declaration not found for port: "<<nodep->prettyName());
|
||||
} else if (!refp->isIO()) {
|
||||
nodep->v3error("Pin is not an in/out/inout: "<<nodep->prettyName());
|
||||
} else if (!refp->isIO() && !refp->isIfaceRef()) {
|
||||
nodep->v3error("Pin is not an in/out/inout/interface: "<<nodep->prettyName());
|
||||
} else {
|
||||
refp->user4(true);
|
||||
VSymEnt* symp = m_statep->insertSym(m_statep->getNodeSym(m_modp),
|
||||
@@ -942,9 +1058,10 @@ public:
|
||||
//======================================================================
|
||||
|
||||
class LinkDotScopeVisitor : public AstNVisitor {
|
||||
private:
|
||||
|
||||
// STATE
|
||||
LinkDotState* m_statep; // State to pass between visitors, including symbol table
|
||||
AstScope* m_scopep; // The current scope
|
||||
VSymEnt* m_modSymp; // Symbol entry for current module
|
||||
|
||||
int debug() { return LinkDotState::debug(); }
|
||||
@@ -960,12 +1077,36 @@ private:
|
||||
// Using the CELL names, we created all hierarchy. We now need to match this Scope
|
||||
// up with the hierarchy created by the CELL names.
|
||||
m_modSymp = m_statep->getScopeSym(nodep);
|
||||
m_scopep = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
m_modSymp = NULL;
|
||||
m_scopep = NULL;
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep, AstNUser*) {
|
||||
if (!nodep->varp()->isFuncLocal()) {
|
||||
m_statep->insertSym(m_modSymp, nodep->varp()->name(), nodep, NULL);
|
||||
VSymEnt* varSymp = m_statep->insertSym(m_modSymp, nodep->varp()->name(), nodep, NULL);
|
||||
if (nodep->varp()->isIfaceRef()
|
||||
&& nodep->varp()->isIfaceParent()) {
|
||||
UINFO(9,"Iface parent ref var "<<nodep->varp()->name()<<" "<<nodep<<endl);
|
||||
// Find the interface cell the var references
|
||||
AstIfaceRefDType* dtypep = nodep->varp()->dtypep()->castIfaceRefDType();
|
||||
if (!dtypep) nodep->v3fatalSrc("Non AstIfaceRefDType on isIfaceRef() var");
|
||||
UINFO(9,"Iface parent dtype "<<dtypep<<endl);
|
||||
string ifcellname = dtypep->cellName();
|
||||
string baddot; VSymEnt* okSymp;
|
||||
VSymEnt* cellSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp);
|
||||
if (!cellSymp) nodep->v3fatalSrc("No symbol for interface cell: " <<nodep->prettyName(ifcellname));
|
||||
UINFO(5, " Found interface cell: se"<<(void*)cellSymp<<" "<<cellSymp->nodep()<<endl);
|
||||
if (dtypep->modportName()!="") {
|
||||
VSymEnt* mpSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp);
|
||||
if (!mpSymp) { nodep->v3fatalSrc("No symbol for interface modport: " <<nodep->prettyName(dtypep->modportName())); }
|
||||
else cellSymp = mpSymp;
|
||||
UINFO(5, " Found modport cell: se"<<(void*)cellSymp<<" "<<mpSymp->nodep()<<endl);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
||||
@@ -976,15 +1117,44 @@ private:
|
||||
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
|
||||
// Track aliases created by V3Inline; if we get a VARXREF(aliased_from)
|
||||
// we'll need to replace it with a VARXREF(aliased_to)
|
||||
if (m_statep->forScopeCreation()) {
|
||||
if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\talias: ");
|
||||
AstVarScope* fromVscp = nodep->lhsp()->castVarRef()->varScopep();
|
||||
AstVarScope* toVscp = nodep->rhsp()->castVarRef()->varScopep();
|
||||
if (!fromVscp || !toVscp) nodep->v3fatalSrc("Bad alias scopes");
|
||||
fromVscp->user2p(toVscp);
|
||||
}
|
||||
if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\talias: ");
|
||||
AstVarScope* fromVscp = nodep->lhsp()->castVarRef()->varScopep();
|
||||
AstVarScope* toVscp = nodep->rhsp()->castVarRef()->varScopep();
|
||||
if (!fromVscp || !toVscp) nodep->v3fatalSrc("Bad alias scopes");
|
||||
fromVscp->user2p(toVscp);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstAssignVarScope* nodep, AstNUser*) {
|
||||
UINFO(5,"ASSIGNVARSCOPE "<<nodep<<endl);
|
||||
if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\tavs: ");
|
||||
VSymEnt* rhsSymp;
|
||||
{
|
||||
AstVarRef* refp = nodep->rhsp()->castVarRef();
|
||||
if (!refp) nodep->v3fatalSrc("Unsupported: Non VarRef attached to interface pin");
|
||||
string scopename = refp->name();
|
||||
string baddot; VSymEnt* okSymp;
|
||||
VSymEnt* symp = m_statep->findDotted(m_modSymp, scopename, baddot, okSymp);
|
||||
if (!symp) nodep->v3fatalSrc("No symbol for interface alias rhs");
|
||||
UINFO(5, " Found a linked scope RHS: "<<scopename<<" se"<<(void*)symp<<" "<<symp->nodep()<<endl);
|
||||
rhsSymp = symp;
|
||||
}
|
||||
VSymEnt* lhsSymp;
|
||||
{
|
||||
AstVarXRef* refp = nodep->lhsp()->castVarXRef();
|
||||
if (!refp) nodep->v3fatalSrc("Unsupported: Non VarXRef attached to interface pin");
|
||||
string scopename = refp->dotted()+"."+refp->name();
|
||||
string baddot; VSymEnt* okSymp;
|
||||
VSymEnt* symp = m_statep->findDotted(m_modSymp, scopename, baddot, okSymp);
|
||||
if (!symp) nodep->v3fatalSrc("No symbol for interface alias lhs");
|
||||
UINFO(5, " Found a linked scope LHS: "<<scopename<<" se"<<(void*)symp<<" "<<symp->nodep()<<endl);
|
||||
lhsSymp = symp;
|
||||
}
|
||||
// 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);
|
||||
// We have stored the link, we don't need these any more
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
||||
}
|
||||
// For speed, don't recurse things that can't have scope
|
||||
// Note we allow AstNodeStmt's as generates may be under them
|
||||
virtual void visit(AstCell*, AstNUser*) {}
|
||||
@@ -1000,6 +1170,7 @@ public:
|
||||
LinkDotScopeVisitor(AstNetlist* rootp, LinkDotState* statep) {
|
||||
UINFO(4,__FUNCTION__<<": "<<endl);
|
||||
m_modSymp = NULL;
|
||||
m_scopep = NULL;
|
||||
m_statep = statep;
|
||||
//
|
||||
rootp->accept(*this);
|
||||
@@ -1009,6 +1180,78 @@ public:
|
||||
|
||||
//======================================================================
|
||||
|
||||
// Iterate an interface to resolve modports
|
||||
class LinkDotIfaceVisitor : public AstNVisitor {
|
||||
// STATE
|
||||
LinkDotState* m_statep; // State to pass between visitors, including symbol table
|
||||
VSymEnt* m_curSymp; // Symbol Entry for current table, where to lookup/insert
|
||||
|
||||
// METHODS
|
||||
int debug() { return LinkDotState::debug(); }
|
||||
|
||||
// VISITs
|
||||
virtual void visit(AstModport* nodep, AstNUser*) {
|
||||
// Modport: Remember its name for later resolution
|
||||
UINFO(5," fiv: "<<nodep<<endl);
|
||||
VSymEnt* oldCurSymp = m_curSymp;
|
||||
{
|
||||
// Create symbol table for the vars
|
||||
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, NULL);
|
||||
m_curSymp->fallbackp(oldCurSymp);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
m_curSymp = oldCurSymp;
|
||||
}
|
||||
virtual void visit(AstModportVarRef* nodep, AstNUser*) {
|
||||
UINFO(5," fiv: "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
VSymEnt* symp = m_curSymp->findIdFallback(nodep->name());
|
||||
if (!symp) {
|
||||
nodep->v3error("Modport item not found: "<<nodep->prettyName());
|
||||
} else if (AstVar* varp = symp->nodep()->castVar()) {
|
||||
// Make symbol under modport that points at the _interface_'s var, not the modport.
|
||||
nodep->varp(varp);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), varp, NULL/*package*/);
|
||||
} else if (AstVarScope* vscp = symp->nodep()->castVarScope()) {
|
||||
// Make symbol under modport that points at the _interface_'s var, not the modport.
|
||||
nodep->varp(vscp->varp());
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), vscp, NULL/*package*/);
|
||||
} else {
|
||||
nodep->v3error("Modport item is not a variable: "<<nodep->prettyName());
|
||||
}
|
||||
if (m_statep->forScopeCreation()) {
|
||||
// Done with AstModportVarRef.
|
||||
// Delete to prevent problems if we dead-delete pointed to variable
|
||||
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
LinkDotIfaceVisitor(AstIface* nodep, VSymEnt* curSymp, LinkDotState* statep) {
|
||||
UINFO(4,__FUNCTION__<<": "<<endl);
|
||||
m_curSymp = curSymp;
|
||||
m_statep = statep;
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~LinkDotIfaceVisitor() {}
|
||||
};
|
||||
|
||||
void LinkDotState::computeIfaceModSyms() {
|
||||
for (IfaceModSyms::iterator it=m_ifaceModSyms.begin(); it!=m_ifaceModSyms.end(); ++it) {
|
||||
AstIface* nodep = it->first;
|
||||
VSymEnt* symp = it->second;
|
||||
LinkDotIfaceVisitor(nodep, symp, this);
|
||||
}
|
||||
m_ifaceModSyms.clear();
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
class LinkDotResolveVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
@@ -1037,6 +1280,7 @@ private:
|
||||
AstCell* m_cellp; // Current cell
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstNodeFTask* m_ftaskp; // Current function/task
|
||||
int m_modportNum; // Uniqueify modport numbers
|
||||
|
||||
struct DotStates {
|
||||
DotPosition m_dotPos; // Scope part of dotted resolution
|
||||
@@ -1087,12 +1331,21 @@ private:
|
||||
}
|
||||
inline void checkNoDot(AstNode* nodep) {
|
||||
if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) {
|
||||
m_statep->preErrorDump();
|
||||
UINFO(1,"ds="<<m_ds.ascii()<<endl);
|
||||
//UINFO(9,"ds="<<m_ds.ascii()<<endl);
|
||||
nodep->v3error("Syntax Error: Not expecting "<<nodep->type()<<" under a "<<nodep->backp()->type()<<" in dotted expression");
|
||||
m_ds.m_dotErr = true;
|
||||
}
|
||||
}
|
||||
AstVar* makeIfaceModportVar(FileLine* fl, AstCell* cellp, AstIface* ifacep, AstModport* modportp) {
|
||||
// Create iface variable, using duplicate var when under same module scope
|
||||
string varName = ifacep->name()+"__Vmp__"+modportp->name()+"__Viftop"+cvtToStr(++m_modportNum);
|
||||
AstIfaceRefDType* idtypep = new AstIfaceRefDType(fl, cellp->name(), ifacep->name(), modportp->name());
|
||||
idtypep->cellp(cellp);
|
||||
AstVar* varp = new AstVar(fl, AstVarType::IFACEREF, varName, VFlagChildDType(), idtypep);
|
||||
varp->isIfaceParent(true);
|
||||
m_modp->addStmtp(varp);
|
||||
return varp;
|
||||
}
|
||||
|
||||
// VISITs
|
||||
virtual void visit(AstNetlist* nodep, AstNUser* vup) {
|
||||
@@ -1108,16 +1361,21 @@ private:
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = m_statep->getNodeSym(nodep); // Until overridden by a SCOPE
|
||||
m_cellp = NULL;
|
||||
m_modp = nodep;
|
||||
m_modportNum = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
m_modp = NULL;
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL;
|
||||
}
|
||||
virtual void visit(AstScope* nodep, AstNUser*) {
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
VSymEnt* oldModSymp = m_modSymp;
|
||||
VSymEnt* oldCurSymp = m_curSymp;
|
||||
checkNoDot(nodep);
|
||||
m_ds.m_dotSymp = m_curSymp = m_statep->getScopeSym(nodep);
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = m_statep->getScopeSym(nodep);
|
||||
nodep->iterateChildren(*this);
|
||||
m_ds.m_dotSymp = m_curSymp = NULL;
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL;
|
||||
m_modSymp = oldModSymp;
|
||||
m_curSymp = oldCurSymp;
|
||||
}
|
||||
virtual void visit(AstCellInline* nodep, AstNUser*) {
|
||||
checkNoDot(nodep);
|
||||
@@ -1126,7 +1384,7 @@ private:
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep, AstNUser*) {
|
||||
// Cell: Resolve its filename. If necessary, parse it.
|
||||
// Cell: Recurse inside or cleanup not founds
|
||||
checkNoDot(nodep);
|
||||
m_cellp = nodep;
|
||||
AstNode::user5ClearTree();
|
||||
@@ -1168,8 +1426,8 @@ private:
|
||||
return;
|
||||
}
|
||||
nodep->v3error("Pin not found: "<<nodep->prettyName());
|
||||
} else if (!refp->isIO() && !refp->isParam()) {
|
||||
nodep->v3error("Pin is not an in/out/inout/param: "<<nodep->prettyName());
|
||||
} else if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) {
|
||||
nodep->v3error("Pin is not an in/out/inout/param/interface: "<<nodep->prettyName());
|
||||
} else {
|
||||
nodep->modVarp(refp);
|
||||
if (refp->user5p() && refp->user5p()->castNode()!=nodep) {
|
||||
@@ -1208,10 +1466,12 @@ private:
|
||||
} else {
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
nodep->lhsp()->iterateAndNext(*this);
|
||||
//if (debug()>=9) nodep->dumpTree("-dot-lho: ");
|
||||
}
|
||||
if (!m_ds.m_dotErr) { // Once something wrong, give up
|
||||
if (start && m_ds.m_dotPos==DP_SCOPE) m_ds.m_dotPos = DP_FINAL; // Top 'final' dot RHS is final RHS, else it's a DOT(DOT(x,*here*),real-rhs) which we consider a RHS
|
||||
nodep->rhsp()->iterateAndNext(*this);
|
||||
//if (debug()>=9) nodep->dumpTree("-dot-rho: ");
|
||||
}
|
||||
if (start) {
|
||||
AstNode* newp;
|
||||
@@ -1295,7 +1555,8 @@ private:
|
||||
} else {
|
||||
foundp = m_ds.m_dotSymp->findIdFallback(nodep->name());
|
||||
}
|
||||
if (foundp) UINFO(9," found=se"<<(void*)foundp<<" n="<<foundp->nodep()<<endl);
|
||||
if (foundp) UINFO(9," found=se"<<(void*)foundp<<" exp="<<expectWhat
|
||||
<<" n="<<foundp->nodep()<<endl);
|
||||
// What fell out?
|
||||
bool ok = false;
|
||||
if (foundp->nodep()->castCell() || foundp->nodep()->castBegin()
|
||||
@@ -1308,9 +1569,41 @@ private:
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
// Upper AstDot visitor will handle it from here
|
||||
}
|
||||
else if (foundp->nodep()->castCell()
|
||||
&& allowVar && m_cellp
|
||||
&& foundp->nodep()->castCell()->modp()->castIface()) {
|
||||
// Interfaces can be referenced like a variable for interconnect
|
||||
AstCell* cellp = foundp->nodep()->castCell();
|
||||
VSymEnt* cellEntp = m_statep->getNodeSym(cellp); if (!cellEntp) nodep->v3fatalSrc("No interface sym entry");
|
||||
VSymEnt* parentEntp = cellEntp->parentp(); // Container of the var; probably a module or generate begin
|
||||
string findName = nodep->name()+"__Viftop";
|
||||
AstVar* ifaceRefVarp = parentEntp->findIdFallback(findName)->nodep()->castVar();
|
||||
if (!ifaceRefVarp) nodep->v3fatalSrc("Can't find interface var ref: "<<findName);
|
||||
//
|
||||
ok = true;
|
||||
if (m_ds.m_dotText!="") m_ds.m_dotText += ".";
|
||||
m_ds.m_dotText += nodep->name();
|
||||
m_ds.m_dotSymp = foundp;
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
UINFO(9," cell -> iface varref "<<foundp->nodep()<<endl);
|
||||
AstNode* newp = new AstVarRef(ifaceRefVarp->fileline(), ifaceRefVarp, false);
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); nodep = NULL;
|
||||
}
|
||||
}
|
||||
else if (AstVar* varp = foundp->nodep()->castVar()) {
|
||||
if (allowVar) {
|
||||
if (AstIfaceRefDType* ifacerefp = varp->subDTypep()->castIfaceRefDType()) {
|
||||
if (!ifacerefp->ifaceViaCellp()) ifacerefp->v3fatalSrc("Unlinked interface");
|
||||
// Really this is a scope reference into an interface
|
||||
UINFO(9,"varref-ifaceref "<<m_ds.m_dotText<<" "<<nodep<<endl);
|
||||
if (m_ds.m_dotText!="") m_ds.m_dotText += ".";
|
||||
m_ds.m_dotText += nodep->name();
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(ifacerefp->ifaceViaCellp());
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
ok = true;
|
||||
AstNode* newp = new AstVarRef(nodep->fileline(), varp, false);
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); nodep = NULL;
|
||||
}
|
||||
else if (allowVar) {
|
||||
AstNodeVarRef* newp;
|
||||
if (m_ds.m_dotText != "") {
|
||||
newp = new AstVarXRef(nodep->fileline(), nodep->name(), m_ds.m_dotText, false); // lvalue'ness computed later
|
||||
@@ -1320,12 +1613,41 @@ private:
|
||||
newp = new AstVarRef(nodep->fileline(), nodep->name(), false); // lvalue'ness computed later
|
||||
newp->varp(varp);
|
||||
newp->packagep(foundp->packagep());
|
||||
UINFO(9," new "<<newp<<endl);
|
||||
}
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); nodep = NULL;
|
||||
m_ds.m_dotPos = DP_MEMBER;
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
else if (AstModport* modportp = foundp->nodep()->castModport()) {
|
||||
// A scope reference into an interface's modport (not necessarily at a pin connection)
|
||||
UINFO(9,"cell-ref-to-modport "<<m_ds.m_dotText<<" "<<nodep<<endl);
|
||||
UINFO(9,"dotSymp "<<m_ds.m_dotSymp<<" "<<m_ds.m_dotSymp->nodep()<<endl);
|
||||
// Iface was the previously dotted component
|
||||
if (!m_ds.m_dotSymp
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()->castIface()) {
|
||||
nodep->v3error("Modport not referenced as <interface>."<<modportp->prettyName());
|
||||
} else if (!m_ds.m_dotSymp->nodep()->castCell()->modp()
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()->castIface()) {
|
||||
nodep->v3error("Modport not referenced from underneath an interface: "<<modportp->prettyName());
|
||||
} else {
|
||||
AstCell* cellp = m_ds.m_dotSymp->nodep()->castCell();
|
||||
if (!cellp) nodep->v3fatalSrc("Modport not referenced from a cell");
|
||||
AstIface* ifacep = cellp->modp()->castIface();
|
||||
//string cellName = m_ds.m_dotText; // Use cellp->name
|
||||
if (m_ds.m_dotText!="") m_ds.m_dotText += ".";
|
||||
m_ds.m_dotText += nodep->name();
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(modportp);
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
ok = true;
|
||||
AstVar* varp = makeIfaceModportVar(nodep->fileline(), cellp, ifacep, modportp);
|
||||
AstVarRef* refp = new AstVarRef(varp->fileline(), varp, false);
|
||||
nodep->replaceWith(refp); pushDeletep(nodep); nodep = NULL;
|
||||
}
|
||||
}
|
||||
else if (AstEnumItem* valuep = foundp->nodep()->castEnumItem()) {
|
||||
if (allowVar) {
|
||||
AstNode* newp = new AstEnumItemRef(nodep->fileline(), valuep, foundp->packagep());
|
||||
@@ -1339,7 +1661,6 @@ private:
|
||||
bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText=="");
|
||||
bool err = !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name()));
|
||||
if (err) {
|
||||
m_statep->preErrorDump();
|
||||
if (foundp) {
|
||||
nodep->v3error("Found definition of '"<<m_ds.m_dotText<<(m_ds.m_dotText==""?"":".")<<nodep->prettyName()
|
||||
<<"'"<<" as a "<<foundp->nodep()->typeName()
|
||||
@@ -1383,7 +1704,6 @@ private:
|
||||
nodep->packagep(foundp->packagep()); // Generally set by parse, but might be an import
|
||||
}
|
||||
if (!nodep->varp()) {
|
||||
m_statep->preErrorDump();
|
||||
nodep->v3error("Can't find definition of signal, again: "<<nodep->prettyName());
|
||||
}
|
||||
}
|
||||
@@ -1407,7 +1727,6 @@ private:
|
||||
string inl = AstNode::dedotName(nodep->inlinedDots());
|
||||
dotSymp = m_statep->findDotted(dotSymp, inl, baddot, okSymp);
|
||||
if (!dotSymp) {
|
||||
m_statep->preErrorDump();
|
||||
nodep->v3fatalSrc("Couldn't resolve inlined scope '"<<baddot<<"' in: "<<nodep->inlinedDots());
|
||||
}
|
||||
}
|
||||
@@ -1418,7 +1737,6 @@ private:
|
||||
nodep->varp(varp);
|
||||
UINFO(7," Resolved "<<nodep<<endl); // Also prints varp
|
||||
if (!nodep->varp()) {
|
||||
m_statep->preErrorDump();
|
||||
nodep->v3error("Can't find definition of '"<<baddot<<"' in dotted signal: "<<nodep->dotted()+"."+nodep->prettyName());
|
||||
okSymp->cellErrorScopes(nodep);
|
||||
}
|
||||
@@ -1427,7 +1745,6 @@ private:
|
||||
VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
|
||||
AstVarScope* vscp = foundp->nodep()->castVarScope(); // maybe NULL
|
||||
if (!vscp) {
|
||||
m_statep->preErrorDump();
|
||||
nodep->v3error("Can't find varpin scope of '"<<baddot<<"' in dotted signal: "<<nodep->dotted()+"."+nodep->prettyName());
|
||||
okSymp->cellErrorScopes(nodep);
|
||||
} else {
|
||||
@@ -1442,6 +1759,7 @@ private:
|
||||
AstVarRef* newvscp = new AstVarRef(nodep->fileline(), vscp, nodep->lvalue());
|
||||
nodep->replaceWith(newvscp);
|
||||
nodep->deleteTree(); nodep=NULL;
|
||||
UINFO(9," new "<<newvscp<<endl); // Also prints taskp
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1493,7 +1811,6 @@ private:
|
||||
UINFO(8,"\t\tInlined "<<inl<<endl);
|
||||
dotSymp = m_statep->findDotted(dotSymp, inl, baddot, okSymp);
|
||||
if (!dotSymp) {
|
||||
m_statep->preErrorDump();
|
||||
okSymp->cellErrorScopes(nodep);
|
||||
nodep->v3fatalSrc("Couldn't resolve inlined scope '"<<baddot<<"' in: "<<nodep->inlinedDots());
|
||||
}
|
||||
@@ -1510,7 +1827,6 @@ private:
|
||||
UINFO(7," Resolved "<<nodep<<endl); // Also prints taskp
|
||||
} else {
|
||||
// Note ParseRef has similar error handling/message output
|
||||
m_statep->preErrorDump();
|
||||
UINFO(7," ErrFtask curSymp=se"<<(void*)m_curSymp<<" dotSymp=se"<<(void*)dotSymp<<endl);
|
||||
if (foundp) {
|
||||
nodep->v3error("Found definition of '"<<m_ds.m_dotText<<(m_ds.m_dotText==""?"":".")<<nodep->prettyName()
|
||||
@@ -1664,6 +1980,7 @@ public:
|
||||
m_cellp = NULL;
|
||||
m_modp = NULL;
|
||||
m_ftaskp = NULL;
|
||||
m_modportNum = 0;
|
||||
//
|
||||
rootp->accept(*this);
|
||||
}
|
||||
@@ -1685,12 +2002,18 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) {
|
||||
LinkDotParamVisitor visitors(rootp,&state);
|
||||
if (LinkDotState::debug()>=5 || v3Global.opt.dumpTree()>=9) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-param.tree"));
|
||||
}
|
||||
else if (step == LDS_ARRAYED) {}
|
||||
else if (step == LDS_SCOPED) {
|
||||
// Well after the initial link when we're ready to operate on the flat design,
|
||||
// process AstScope's. This needs to be separate pass after whole hierarchy graph created.
|
||||
LinkDotScopeVisitor visitors(rootp,&state);
|
||||
if (LinkDotState::debug()>=5 || v3Global.opt.dumpTree()>=9) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-scoped.tree"));
|
||||
}
|
||||
else v3fatalSrc("Bad case");
|
||||
state.dump();
|
||||
state.computeIfaceModSyms();
|
||||
state.computeIfaceVarSyms();
|
||||
state.computeScopeAliases();
|
||||
state.dump();
|
||||
LinkDotResolveVisitor visitorb(rootp,&state);
|
||||
}
|
||||
|
||||
+1
-1
@@ -227,7 +227,7 @@ private:
|
||||
}
|
||||
}
|
||||
//if (debug()>=9) { UINFO(0,"\n"); beginp->dumpTree(cout," labeli: "); }
|
||||
if (!beginp) { nodep->v3error("disable isn't underneath a begin with name: "<<nodep->name()); }
|
||||
if (!beginp) { nodep->v3error("disable isn't underneath a begin with name: "<<nodep->prettyName()); }
|
||||
else {
|
||||
// Jump to the end of the named begin
|
||||
AstJumpLabel* labelp = findAddLabel(beginp, false);
|
||||
|
||||
+19
-4
@@ -67,9 +67,11 @@ void V3LinkLevel::modSortByLevel() {
|
||||
}
|
||||
vec.push_back(nodep);
|
||||
}
|
||||
sort(vec.begin(), vec.end(), CmpLevel()); // Sort the vector
|
||||
stable_sort(vec.begin(), vec.end(), CmpLevel()); // Sort the vector
|
||||
UINFO(9,"modSortByLevel() sorted\n"); // Comment required for gcc4.6.3 / bug666
|
||||
for (ModVec::iterator it = vec.begin(); it != vec.end(); ++it) {
|
||||
AstNodeModule* nodep = *it;
|
||||
nodep->clearIter(); // Because we didn't iterate to find the node pointers, may have a stale m_iterp() needing cleanup
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
if (v3Global.rootp()->modulesp()) v3Global.rootp()->v3fatalSrc("Unlink didn't work");
|
||||
@@ -77,6 +79,7 @@ void V3LinkLevel::modSortByLevel() {
|
||||
AstNodeModule* nodep = *it;
|
||||
v3Global.rootp()->addModulep(nodep);
|
||||
}
|
||||
UINFO(9,"modSortByLevel() done\n"); // Comment required for gcc4.6.3 / bug666
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
@@ -95,6 +98,18 @@ void V3LinkLevel::wrapTop(AstNetlist* netlistp) {
|
||||
newmodp->modPublic(true);
|
||||
netlistp->addModulep(newmodp);
|
||||
|
||||
// TODO the module creation above could be done after linkcells, but
|
||||
// the rest must be done after data type resolution
|
||||
wrapTopCell(netlistp);
|
||||
wrapTopPackages(netlistp);
|
||||
}
|
||||
|
||||
void V3LinkLevel::wrapTopCell(AstNetlist* netlistp) {
|
||||
AstNodeModule* newmodp = netlistp->modulesp();
|
||||
if (!newmodp || !newmodp->isTop()) netlistp->v3fatalSrc("No TOP module found to process");
|
||||
AstNodeModule* oldmodp = newmodp->nextp()->castNodeModule();
|
||||
if (!oldmodp) netlistp->v3fatalSrc("No module found to process");
|
||||
|
||||
// Add instance
|
||||
AstCell* cellp = new AstCell(newmodp->fileline(),
|
||||
(v3Global.opt.l2Name() ? "v" : oldmodp->name()),
|
||||
@@ -126,13 +141,13 @@ void V3LinkLevel::wrapTop(AstNetlist* netlistp) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wrapTopPackages(netlistp, newmodp);
|
||||
}
|
||||
|
||||
void V3LinkLevel::wrapTopPackages(AstNetlist* netlistp, AstNodeModule* newmodp) {
|
||||
void V3LinkLevel::wrapTopPackages(AstNetlist* netlistp) {
|
||||
// Instantiate all packages under the top wrapper
|
||||
// This way all later SCOPE based optimizations can ignore packages
|
||||
AstNodeModule* newmodp = netlistp->modulesp();
|
||||
if (!newmodp || !newmodp->isTop()) netlistp->v3fatalSrc("No TOP module found to process");
|
||||
for (AstNodeModule* modp = netlistp->modulesp(); modp; modp=modp->nextp()->castNodeModule()) {
|
||||
if (modp->castPackage()) {
|
||||
AstCell* cellp = new AstCell(modp->fileline(),
|
||||
|
||||
+2
-1
@@ -29,7 +29,8 @@
|
||||
|
||||
class V3LinkLevel {
|
||||
private:
|
||||
static void wrapTopPackages(AstNetlist* nodep, AstNodeModule* newmodp);
|
||||
static void wrapTopCell(AstNetlist* nodep);
|
||||
static void wrapTopPackages(AstNetlist* nodep);
|
||||
public:
|
||||
static void modSortByLevel();
|
||||
static void wrapTop(AstNetlist* nodep);
|
||||
|
||||
@@ -152,6 +152,12 @@ private:
|
||||
nodep->valuep()->unlinkFrBack()));
|
||||
}
|
||||
}
|
||||
if (nodep->isIfaceRef() && !nodep->isIfaceParent()) {
|
||||
// Only AstIfaceRefDType's at this point correspond to ports;
|
||||
// haven't made additional ones for interconnect yet, so assert is simple
|
||||
// What breaks later is we don't have a Scope/Cell representing the interface to attach to
|
||||
if (m_modp->level()<=2) nodep->v3error("Unsupported: Interfaced port on top level module");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstAttrOf* nodep, AstNUser*) {
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ private:
|
||||
} else {
|
||||
string rsvd = m_words.isKeyword(nodep->name());
|
||||
if (rsvd != "") {
|
||||
nodep->v3warn(SYMRSVDWORD,"Symbol matches "+rsvd+": '"<<nodep->name()<<"'");
|
||||
nodep->v3warn(SYMRSVDWORD,"Symbol matches "+rsvd+": '"<<nodep->prettyName()<<"'");
|
||||
string newname = (string)"__SYM__"+nodep->name();
|
||||
nodep->name(newname);
|
||||
}
|
||||
|
||||
+1
-1
@@ -515,7 +515,7 @@ uint32_t V3Number::toUInt() const {
|
||||
|
||||
double V3Number::toDouble() const {
|
||||
if (VL_UNLIKELY(!isDouble())) {
|
||||
m_fileline->v3fatalSrc("Real conversion on non real number");
|
||||
m_fileline->v3fatalSrc("Real conversion on non-real number");
|
||||
}
|
||||
if (VL_UNLIKELY(width()!=64)) {
|
||||
m_fileline->v3fatalSrc("Real operation on wrong sized number");
|
||||
|
||||
+14
-3
@@ -46,7 +46,8 @@
|
||||
//######################################################################
|
||||
// V3 Internal state
|
||||
|
||||
struct V3OptionsImp {
|
||||
class V3OptionsImp {
|
||||
public:
|
||||
// TYPES
|
||||
typedef std::map<string,set<string> > DirMap; // Directory listing
|
||||
|
||||
@@ -91,6 +92,7 @@ struct V3OptionsImp {
|
||||
}
|
||||
}
|
||||
V3OptionsImp() {}
|
||||
~V3OptionsImp() {}
|
||||
};
|
||||
|
||||
void V3Options::addIncDirUser(const string& incdir) {
|
||||
@@ -673,14 +675,15 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
else if ( !strncmp (sw, "+incdir+", 8)) {
|
||||
addIncDirUser (parseFileArg(optdir, string (sw+strlen("+incdir+"))));
|
||||
}
|
||||
else if (parseLangExt(sw, "+systemverilogext+", V3LangCode::L1800_2009)
|
||||
else if (parseLangExt(sw, "+systemverilogext+", V3LangCode::L1800_2012)
|
||||
|| parseLangExt(sw, "+verilog1995ext+", V3LangCode::L1364_1995)
|
||||
|| parseLangExt(sw, "+verilog2001ext+", V3LangCode::L1364_2001)
|
||||
|| parseLangExt(sw, "+1364-1995ext+", V3LangCode::L1364_1995)
|
||||
|| parseLangExt(sw, "+1364-2001ext+", V3LangCode::L1364_2001)
|
||||
|| parseLangExt(sw, "+1364-2005ext+", V3LangCode::L1364_2005)
|
||||
|| parseLangExt(sw, "+1800-2005ext+", V3LangCode::L1800_2005)
|
||||
|| parseLangExt(sw, "+1800-2009ext+", V3LangCode::L1800_2009)) {
|
||||
|| parseLangExt(sw, "+1800-2009ext+", V3LangCode::L1800_2009)
|
||||
|| parseLangExt(sw, "+1800-2012ext+", V3LangCode::L1800_2012)) {
|
||||
// Nothing to do here - all done in the test
|
||||
|
||||
}
|
||||
@@ -735,12 +738,16 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
else if ( onoff (sw, "-l2name", flag/*ref*/) ) { m_l2Name = flag; }
|
||||
else if ( onoff (sw, "-lint-only", flag/*ref*/) ) { m_lintOnly = flag; }
|
||||
else if ( !strcmp (sw, "-no-pins64") ) { m_pinsBv = 33; }
|
||||
else if ( onoff (sw, "-order-clock-delay", flag/*ref*/) ) { m_orderClockDly = flag; }
|
||||
else if ( !strcmp (sw, "-pins64") ) { m_pinsBv = 65; }
|
||||
else if ( onoff (sw, "-pins-sc-uint", flag/*ref*/) ){ m_pinsScUint = flag; if (!m_pinsScBigUint) m_pinsBv = 65; }
|
||||
else if ( onoff (sw, "-pins-sc-biguint", flag/*ref*/) ){ m_pinsScBigUint = flag; m_pinsBv = 513; }
|
||||
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, "-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; }
|
||||
else if ( !strcmp (sw, "-sc") ) { m_outFormatOk = true; m_systemC = true; m_systemPerl = false; }
|
||||
else if ( onoff (sw, "-skip-identical", flag/*ref*/) ) { m_skipIdentical = flag; }
|
||||
@@ -765,6 +772,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
case 'a': m_oTable = flag; break;
|
||||
case 'b': m_oCombine = flag; break;
|
||||
case 'c': m_oConst = flag; break;
|
||||
case 'd': m_oDedupe = flag; break;
|
||||
case 'e': m_oCase = flag; break;
|
||||
case 'f': m_oFlopGater = flag; break;
|
||||
case 'g': m_oGate = flag; break;
|
||||
@@ -1196,6 +1204,7 @@ V3Options::V3Options() {
|
||||
m_lintOnly = false;
|
||||
m_makeDepend = true;
|
||||
m_makePhony = false;
|
||||
m_orderClockDly = true;
|
||||
m_outFormatOk = false;
|
||||
m_warnFatal = true;
|
||||
m_pinsBv = 65;
|
||||
@@ -1212,6 +1221,7 @@ V3Options::V3Options() {
|
||||
m_traceDups = false;
|
||||
m_traceUnderscore = false;
|
||||
m_underlineZero = false;
|
||||
m_reportUnoptflat = false;
|
||||
m_xInitialEdge = false;
|
||||
m_xmlOnly = false;
|
||||
|
||||
@@ -1301,6 +1311,7 @@ void V3Options::optimize(int level) {
|
||||
m_oSubst = flag;
|
||||
m_oSubstConst = flag;
|
||||
m_oTable = flag;
|
||||
m_oDedupe = flag;
|
||||
// And set specific optimization levels
|
||||
if (level >= 3) {
|
||||
m_inlineMult = -1; // Maximum inlining
|
||||
|
||||
@@ -74,8 +74,11 @@ class V3Options {
|
||||
bool m_inhibitSim; // main switch: --inhibit-sim
|
||||
bool m_l2Name; // main switch: --l2name
|
||||
bool m_lintOnly; // main switch: --lint-only
|
||||
bool m_orderClockDly;// main switch: --order-clock-delay
|
||||
bool m_outFormatOk; // main switch: --cc, --sc or --sp was specified
|
||||
bool m_warnFatal; // main switch: --warnFatal
|
||||
bool m_pinsScUint; // main switch: --pins-sc-uint
|
||||
bool m_pinsScBigUint;// main switch: --pins-sc-biguint
|
||||
bool m_pinsUint8; // main switch: --pins-uint8
|
||||
bool m_profileCFuncs;// main switch: --profile-cfuncs
|
||||
bool m_psl; // main switch: --psl
|
||||
@@ -89,6 +92,7 @@ class V3Options {
|
||||
bool m_traceDups; // main switch: --trace-dups
|
||||
bool m_traceUnderscore;// main switch: --trace-underscore
|
||||
bool m_underlineZero;// main switch: --underline-zero; undocumented old Verilator 2
|
||||
bool m_reportUnoptflat; // main switch: --report-unoptflat
|
||||
bool m_xInitialEdge; // main switch: --x-initial-edge
|
||||
bool m_xmlOnly; // main switch: --xml-netlist
|
||||
|
||||
@@ -131,6 +135,7 @@ class V3Options {
|
||||
bool m_oCase; // main switch: -Oe: case tree conversion
|
||||
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_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
|
||||
@@ -210,9 +215,12 @@ class V3Options {
|
||||
bool trace() const { return m_trace; }
|
||||
bool traceDups() const { return m_traceDups; }
|
||||
bool traceUnderscore() const { return m_traceUnderscore; }
|
||||
bool orderClockDly() const { return m_orderClockDly; }
|
||||
bool outFormatOk() const { return m_outFormatOk; }
|
||||
bool keepTempFiles() const { return (V3Error::debugDefault()!=0); }
|
||||
bool warnFatal() const { return m_warnFatal; }
|
||||
bool pinsScUint() const { return m_pinsScUint; }
|
||||
bool pinsScBigUint() const { return m_pinsScBigUint; }
|
||||
bool pinsUint8() const { return m_pinsUint8; }
|
||||
bool profileCFuncs() const { return m_profileCFuncs; }
|
||||
bool psl() const { return m_psl; }
|
||||
@@ -221,6 +229,7 @@ class V3Options {
|
||||
bool lintOnly() const { return m_lintOnly; }
|
||||
bool ignc() const { return m_ignc; }
|
||||
bool inhibitSim() const { return m_inhibitSim; }
|
||||
bool reportUnoptflat() const { return m_reportUnoptflat; }
|
||||
bool xInitialEdge() const { return m_xInitialEdge; }
|
||||
bool xmlOnly() const { return m_xmlOnly; }
|
||||
|
||||
@@ -266,6 +275,7 @@ class V3Options {
|
||||
bool oCase() const { return m_oCase; }
|
||||
bool oCombine() const { return m_oCombine; }
|
||||
bool oConst() const { return m_oConst; }
|
||||
bool oDedupe() const { return m_oDedupe; }
|
||||
bool oExpand() const { return m_oExpand; }
|
||||
bool oFlopGater() const { return m_oFlopGater; }
|
||||
bool oGate() const { return m_oGate; }
|
||||
|
||||
+181
-18
@@ -232,6 +232,26 @@ public:
|
||||
~OrderUser() {}
|
||||
};
|
||||
|
||||
|
||||
//######################################################################
|
||||
// Comparator classes
|
||||
|
||||
//! Comparator for width of associated variable
|
||||
struct OrderVarWidthCmp {
|
||||
bool operator() (OrderVarStdVertex* vsv1p, OrderVarStdVertex* vsv2p) {
|
||||
return vsv1p->varScp()->varp()->width()
|
||||
> vsv2p->varScp()->varp()->width();
|
||||
}
|
||||
};
|
||||
|
||||
//! Comparator for fanout of vertex
|
||||
struct OrderVarFanoutCmp {
|
||||
bool operator() (OrderVarStdVertex* vsv1p, OrderVarStdVertex* vsv2p) {
|
||||
return vsv1p->fanout() > vsv2p->fanout();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//######################################################################
|
||||
// Order class functions
|
||||
|
||||
@@ -285,6 +305,7 @@ private:
|
||||
protected:
|
||||
friend class OrderMoveDomScope;
|
||||
V3List<OrderMoveDomScope*> m_pomReadyDomScope; // List of ready domain/scope pairs, by loopId
|
||||
vector<OrderVarStdVertex*> m_unoptflatVars; // Vector of variables in UNOPTFLAT loop
|
||||
|
||||
private:
|
||||
// STATS
|
||||
@@ -354,8 +375,10 @@ private:
|
||||
void processBrokeLoop();
|
||||
#endif
|
||||
void processCircular();
|
||||
typedef deque<OrderEitherVertex*> VertexVec;
|
||||
void processInputs();
|
||||
void processInputsIterate(OrderEitherVertex* vertexp);
|
||||
void processInputsInIterate(OrderEitherVertex* vertexp, VertexVec& todoVec);
|
||||
void processInputsOutIterate(OrderEitherVertex* vertexp, VertexVec& todoVec);
|
||||
void processSensitive();
|
||||
void processDomains();
|
||||
void processDomainsIterate(OrderEitherVertex* vertexp);
|
||||
@@ -418,10 +441,99 @@ private:
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Find all variables in an UNOPTFLAT loop
|
||||
//!
|
||||
//! Ignore vars that are 1-bit wide and don't worry about generated
|
||||
//! variables (PRE and POST vars, __Vdly__, __Vcellin__ and __VCellout).
|
||||
//! What remains are candidates for splitting to break loops.
|
||||
//!
|
||||
//! node->user3 is used to mark if we have done a particular variable.
|
||||
//! vertex->user is used to mark if we have seen this vertex before.
|
||||
//!
|
||||
//! @todo We could be cleverer in the future and consider just
|
||||
//! the width that is generated/consumed.
|
||||
void reportLoopVars(OrderVarVertex* vertexp) {
|
||||
m_graph.userClearVertices();
|
||||
AstNode::user3ClearTree();
|
||||
m_unoptflatVars.clear();
|
||||
reportLoopVarsIterate (vertexp, vertexp->color());
|
||||
AstNode::user3ClearTree();
|
||||
m_graph.userClearVertices();
|
||||
// May be very large vector, so only report the "most important"
|
||||
// elements. Up to 10 of the widest
|
||||
cerr<<V3Error::msgPrefix()
|
||||
<<" Widest candidate vars to split:"<<endl;
|
||||
std::stable_sort (m_unoptflatVars.begin(), m_unoptflatVars.end(), OrderVarWidthCmp());
|
||||
int lim = m_unoptflatVars.size() < 10 ? m_unoptflatVars.size() : 10;
|
||||
for (int i = 0; i < lim; i++) {
|
||||
OrderVarStdVertex* vsvertexp = m_unoptflatVars[i];
|
||||
AstVar* varp = vsvertexp->varScp()->varp();
|
||||
cerr<<V3Error::msgPrefix()<<" "
|
||||
<<varp->fileline()<<" "<<varp->prettyName()<<dec
|
||||
<<", width "<<varp->width()<<", fanout "
|
||||
<<vsvertexp->fanout()<<endl;
|
||||
}
|
||||
// Up to 10 of the most fanned out
|
||||
cerr<<V3Error::msgPrefix()
|
||||
<<" Most fanned out candidate vars to split:"<<endl;
|
||||
std::stable_sort (m_unoptflatVars.begin(), m_unoptflatVars.end(),
|
||||
OrderVarFanoutCmp());
|
||||
lim = m_unoptflatVars.size() < 10 ? m_unoptflatVars.size() : 10;
|
||||
for (int i = 0; i < lim; i++) {
|
||||
OrderVarStdVertex* vsvertexp = m_unoptflatVars[i];
|
||||
AstVar* varp = vsvertexp->varScp()->varp();
|
||||
cerr<<V3Error::msgPrefix()<<" "
|
||||
<<varp->fileline()<<" "<<varp->prettyName()
|
||||
<<", width "<<dec<<varp->width()
|
||||
<<", fanout "<<vsvertexp->fanout()<<endl;
|
||||
}
|
||||
m_unoptflatVars.clear();
|
||||
}
|
||||
|
||||
void reportLoopVarsIterate(V3GraphVertex* vertexp, uint32_t color) {
|
||||
if (vertexp->user()) return; // Already done
|
||||
vertexp->user(1);
|
||||
if (OrderVarStdVertex* vsvertexp = dynamic_cast<OrderVarStdVertex*>(vertexp)) {
|
||||
// Only reporting on standard variable vertices
|
||||
AstVar* varp = vsvertexp->varScp()->varp();
|
||||
if (!varp->user3()) {
|
||||
string name = varp->prettyName();
|
||||
if ((varp->width() != 1)
|
||||
&& (name.find("__Vdly") == string::npos)
|
||||
&& (name.find("__Vcell") == string::npos)) {
|
||||
// Variable to report on and not yet done
|
||||
m_unoptflatVars.push_back(vsvertexp);
|
||||
}
|
||||
varp->user3Inc();
|
||||
}
|
||||
}
|
||||
// Iterate through all the to and from vertices of the same color
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep;
|
||||
edgep = edgep->outNextp()) {
|
||||
if (edgep->top()->color() == color) {
|
||||
reportLoopVarsIterate(edgep->top(), color);
|
||||
}
|
||||
}
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep;
|
||||
edgep = edgep->inNextp()) {
|
||||
if (edgep->fromp()->color() == color) {
|
||||
reportLoopVarsIterate(edgep->fromp(), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep, AstNUser*) {
|
||||
{
|
||||
@@ -585,6 +697,13 @@ private:
|
||||
OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD);
|
||||
if (m_inPost) {
|
||||
new OrderPostCutEdge(&m_graph, m_logicVxp, varVxp);
|
||||
// Mark the vertex. Used to control marking
|
||||
// internal clocks circular, which must only
|
||||
// happen if they are generated by delayed
|
||||
// assignment.
|
||||
UINFO(5, " Found delayed assignment (post) "
|
||||
<< varVxp << endl);
|
||||
varVxp->isDelayed(true);
|
||||
} else {
|
||||
new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp);
|
||||
}
|
||||
@@ -949,28 +1068,33 @@ void OrderVisitor::processBrokeLoop() {
|
||||
// Clock propagation
|
||||
|
||||
void OrderVisitor::processInputs() {
|
||||
m_graph.userClearVertices(); // Vertex::user() // true if processed
|
||||
m_graph.userClearVertices(); // Vertex::user() // 1 if input recursed, 2 if marked as input, 3 if out-edges recursed
|
||||
// Start at input vertex, process from input-to-output order
|
||||
VertexVec todoVec; // List of newly-input marked vectors we need to process
|
||||
todoVec.push_front(m_inputsVxp);
|
||||
m_inputsVxp->isFromInput(true); // By definition
|
||||
processInputsIterate(m_inputsVxp);
|
||||
while (!todoVec.empty()) {
|
||||
OrderEitherVertex* vertexp = todoVec.back(); todoVec.pop_back();
|
||||
processInputsOutIterate(vertexp, todoVec);
|
||||
}
|
||||
}
|
||||
|
||||
void OrderVisitor::processInputsIterate(OrderEitherVertex* vertexp) {
|
||||
void OrderVisitor::processInputsInIterate(OrderEitherVertex* vertexp, VertexVec& todoVec) {
|
||||
// Propagate PrimaryIn through simple assignments
|
||||
if (vertexp->user()) return; // Already processed
|
||||
if (0 && debug()>=9) {
|
||||
UINFO(9," InIt "<<vertexp<<endl);
|
||||
UINFO(9," InIIter "<<vertexp<<endl);
|
||||
if (OrderLogicVertex* vvertexp = dynamic_cast<OrderLogicVertex*>(vertexp)) {
|
||||
vvertexp->nodep()->dumpTree(cout,"- TT: ");
|
||||
}
|
||||
}
|
||||
vertexp->user(true); // Processing
|
||||
vertexp->user(1); // Processing
|
||||
// First handle all inputs to this vertex, in most cases they'll be already processed earlier
|
||||
// Also, determine if this vertex is an input
|
||||
int inonly = 1; // 0=no, 1=maybe, 2=yes until a no
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
|
||||
OrderEitherVertex* frVertexp = (OrderEitherVertex*)edgep->fromp();
|
||||
processInputsIterate(frVertexp);
|
||||
processInputsInIterate(frVertexp, todoVec);
|
||||
if (frVertexp->isFromInput()) {
|
||||
if (inonly==1) inonly = 2;
|
||||
} else if (dynamic_cast<OrderVarPostVertex*>(frVertexp)) {
|
||||
@@ -981,20 +1105,38 @@ void OrderVisitor::processInputsIterate(OrderEitherVertex* vertexp) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (inonly == 2) { // Set it. Note may have already been set earlier, too
|
||||
|
||||
if (inonly == 2 && vertexp->user()<2) { // Set it. Note may have already been set earlier, too
|
||||
UINFO(9," Input reassignment: "<<vertexp<<endl);
|
||||
vertexp->isFromInput(true);
|
||||
vertexp->user(2); // 2 means on list
|
||||
// Can't work on out-edges of a node we know is an input immediately,
|
||||
// as it might visit other nodes before their input state is resolved.
|
||||
// So push to list and work on it later when all in-edges known resolved
|
||||
todoVec.push_back(vertexp);
|
||||
}
|
||||
// If we're still an input, process all targets of this vertex
|
||||
if (vertexp->isFromInput()) {
|
||||
//UINFO(9," InIdone "<<vertexp<<endl);
|
||||
}
|
||||
|
||||
void OrderVisitor::processInputsOutIterate(OrderEitherVertex* vertexp, VertexVec& todoVec) {
|
||||
if (vertexp->user()==3) return; // Already out processed
|
||||
//UINFO(9," InOIter "<<vertexp<<endl);
|
||||
// First make sure input path is fully recursed
|
||||
processInputsInIterate(vertexp, todoVec);
|
||||
// Propagate PrimaryIn through simple assignments
|
||||
if (!vertexp->isFromInput()) v3fatalSrc("processInputsOutIterate only for input marked vertexes");
|
||||
vertexp->user(3); // out-edges processed
|
||||
|
||||
{
|
||||
// Propagate PrimaryIn through simple assignments, followint target of vertex
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||
OrderEitherVertex* toVertexp = (OrderEitherVertex*)edgep->top();
|
||||
if (OrderVarStdVertex* vvertexp = dynamic_cast<OrderVarStdVertex*>(toVertexp)) {
|
||||
processInputsIterate(vvertexp);
|
||||
processInputsInIterate(vvertexp, todoVec);
|
||||
}
|
||||
if (OrderLogicVertex* vvertexp = dynamic_cast<OrderLogicVertex*>(toVertexp)) {
|
||||
if (vvertexp->nodep()->castNodeAssign()) {
|
||||
processInputsIterate(vvertexp);
|
||||
processInputsInIterate(vvertexp, todoVec);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1011,10 +1153,25 @@ void OrderVisitor::processCircular() {
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
if (OrderVarStdVertex* vvertexp = dynamic_cast<OrderVarStdVertex*>(itp)) {
|
||||
if (vvertexp->isClock() && !vvertexp->isFromInput()) {
|
||||
// If a clock is generated internally, we need to do another loop
|
||||
// through the entire evaluation. This fixes races; see t_clk_dpulse test.
|
||||
UINFO(5,"Circular Clock "<<vvertexp<<endl);
|
||||
nodeMarkCircular(vvertexp, NULL);
|
||||
// If a clock is generated internally, we need to do another
|
||||
// loop through the entire evaluation. This fixes races; see
|
||||
// t_clk_dpulse test.
|
||||
//
|
||||
// This all seems to hinge on how the clock is generated. If
|
||||
// it is generated by delayed assignment, we need the loop. If
|
||||
// it is combinatorial, we do not (and indeed it will break
|
||||
// other tests such as t_gated_clk_1.
|
||||
if (!v3Global.opt.orderClockDly()) {
|
||||
UINFO(5,"Circular Clock, no-order-clock-delay "<<vvertexp<<endl);
|
||||
nodeMarkCircular(vvertexp, NULL);
|
||||
}
|
||||
else if (vvertexp->isDelayed()) {
|
||||
UINFO(5,"Circular Clock, delayed "<<vvertexp<<endl);
|
||||
nodeMarkCircular(vvertexp, NULL);
|
||||
}
|
||||
else {
|
||||
UINFO(5,"Circular Clock, not delayed "<<vvertexp<<endl);
|
||||
}
|
||||
}
|
||||
// Also mark any cut edges
|
||||
for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||
@@ -1189,7 +1346,7 @@ void OrderVisitor::processEdgeReport() {
|
||||
}
|
||||
|
||||
*logp<<"Signals and their clock domains:"<<endl;
|
||||
sort(report.begin(), report.end());
|
||||
stable_sort(report.begin(), report.end());
|
||||
for (deque<string>::iterator it=report.begin(); it!=report.end(); ++it) {
|
||||
*logp<<(*it)<<endl;
|
||||
}
|
||||
@@ -1600,7 +1757,10 @@ void OrderVisitor::process() {
|
||||
m_graph.acyclic(&V3GraphEdge::followAlwaysTrue);
|
||||
m_graph.dumpDotFilePrefixed("orderg_preasn_done", true);
|
||||
#else
|
||||
// Break cycles
|
||||
// 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
|
||||
// edges are actually still there, just with weight 0).
|
||||
UINFO(2," Acyclic & Order...\n");
|
||||
m_graph.acyclic(&V3GraphEdge::followAlwaysTrue);
|
||||
m_graph.dumpDotFilePrefixed("orderg_acyc");
|
||||
@@ -1611,6 +1771,9 @@ void OrderVisitor::process() {
|
||||
m_graph.order();
|
||||
m_graph.dumpDotFilePrefixed("orderg_order");
|
||||
|
||||
// This finds everything that can be traced from an input (which by
|
||||
// definition are the source clocks). After this any vertex which was
|
||||
// traced has isFromInput() true.
|
||||
UINFO(2," Process Clocks...\n");
|
||||
processInputs(); // must be before processCircular
|
||||
|
||||
|
||||
+110
-41
@@ -123,6 +123,15 @@ public:
|
||||
virtual void loopsVertexCb(V3GraphVertex* vertexp);
|
||||
};
|
||||
|
||||
//! Graph for UNOPTFLAT loops
|
||||
class UnoptflatGraph : public OrderGraph {
|
||||
public:
|
||||
UnoptflatGraph() {}
|
||||
virtual ~UnoptflatGraph() {}
|
||||
// Methods
|
||||
virtual void loopsVertexCb(V3GraphVertex* vertexp);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Vertex types
|
||||
|
||||
@@ -131,12 +140,16 @@ class OrderEitherVertex : public V3GraphVertex {
|
||||
AstSenTree* m_domainp; // Clock domain (NULL = to be computed as we iterate)
|
||||
OrderLoopId m_inLoop; // Loop number vertex is in
|
||||
bool m_isFromInput; // From input, or derrived therefrom (conservatively false)
|
||||
protected:
|
||||
OrderEitherVertex(V3Graph* graphp, const OrderEitherVertex& old)
|
||||
: V3GraphVertex(graphp, old), m_scopep(old.m_scopep), m_domainp(old.m_domainp)
|
||||
, m_inLoop(old.m_inLoop), m_isFromInput(old.m_isFromInput) {}
|
||||
public:
|
||||
OrderEitherVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp)
|
||||
: V3GraphVertex(graphp), m_scopep(scopep), m_domainp(domainp)
|
||||
, m_inLoop(LOOPID_UNKNOWN), m_isFromInput(false) {
|
||||
}
|
||||
, m_inLoop(LOOPID_UNKNOWN), m_isFromInput(false) {}
|
||||
virtual ~OrderEitherVertex() {}
|
||||
virtual OrderEitherVertex* clone(V3Graph* graphp) const = 0;
|
||||
// Methods
|
||||
virtual OrderVEdgeType type() const = 0;
|
||||
virtual bool domainMatters() = 0; // Must be in same domain when cross edge to this vertex
|
||||
@@ -152,12 +165,16 @@ public:
|
||||
};
|
||||
|
||||
class OrderInputsVertex : public OrderEitherVertex {
|
||||
OrderInputsVertex(V3Graph* graphp, const OrderInputsVertex& old)
|
||||
: OrderEitherVertex(graphp, old) {}
|
||||
public:
|
||||
OrderInputsVertex(V3Graph* graphp, AstSenTree* domainp)
|
||||
: OrderEitherVertex(graphp, NULL, domainp) {
|
||||
isFromInput(true); // By definition
|
||||
}
|
||||
virtual ~OrderInputsVertex() {}
|
||||
virtual OrderInputsVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderInputsVertex(graphp, *this); }
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_INPUTS; }
|
||||
virtual string name() const { return "*INPUTS*"; }
|
||||
virtual string dotColor() const { return "green"; }
|
||||
@@ -166,10 +183,14 @@ public:
|
||||
};
|
||||
|
||||
class OrderSettleVertex : public OrderEitherVertex {
|
||||
OrderSettleVertex(V3Graph* graphp, const OrderSettleVertex& old)
|
||||
: OrderEitherVertex(graphp, old) {}
|
||||
public:
|
||||
OrderSettleVertex(V3Graph* graphp, AstSenTree* domainp)
|
||||
: OrderEitherVertex(graphp, NULL, domainp) {}
|
||||
virtual ~OrderSettleVertex() {}
|
||||
virtual OrderSettleVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderSettleVertex(graphp, *this); }
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_SETTLE; }
|
||||
virtual string name() const { return "*SETTLE*"; }
|
||||
virtual string dotColor() const { return "green"; }
|
||||
@@ -180,10 +201,15 @@ public:
|
||||
class OrderLogicVertex : public OrderEitherVertex {
|
||||
AstNode* m_nodep;
|
||||
OrderMoveVertex* m_moveVxp;
|
||||
protected:
|
||||
OrderLogicVertex(V3Graph* graphp, const OrderLogicVertex& old)
|
||||
: OrderEitherVertex(graphp, old), m_nodep(old.m_nodep), m_moveVxp(old.m_moveVxp) {}
|
||||
public:
|
||||
OrderLogicVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp, AstNode* nodep)
|
||||
: OrderEitherVertex(graphp, scopep, domainp), m_nodep(nodep), m_moveVxp(NULL) {}
|
||||
virtual ~OrderLogicVertex() {}
|
||||
virtual OrderLogicVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderLogicVertex(graphp, *this); }
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_LOGIC; }
|
||||
virtual bool domainMatters() { return true; }
|
||||
// Accessors
|
||||
@@ -198,11 +224,15 @@ class OrderVarVertex : public OrderEitherVertex {
|
||||
AstVarScope* m_varScp;
|
||||
OrderVarVertex* m_pilNewVertexp; // for processInsLoopNewVar
|
||||
bool m_isClock; // Used as clock
|
||||
bool m_isDelayed; // Set in a delayed assignment
|
||||
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) {}
|
||||
public:
|
||||
OrderVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: OrderEitherVertex(graphp, scopep, NULL), m_varScp(varScp)
|
||||
, m_pilNewVertexp(NULL), m_isClock(false)
|
||||
{}
|
||||
, m_pilNewVertexp(NULL), m_isClock(false), m_isDelayed(false) {}
|
||||
virtual ~OrderVarVertex() {}
|
||||
virtual OrderVarVertex* clone (V3Graph* graphp) const = 0;
|
||||
virtual OrderVEdgeType type() const = 0;
|
||||
@@ -210,71 +240,78 @@ public:
|
||||
AstVarScope* varScp() const { return m_varScp; }
|
||||
void isClock(bool flag) { m_isClock=flag; }
|
||||
bool isClock() const { return m_isClock; }
|
||||
void isDelayed(bool flag) { m_isDelayed=flag; }
|
||||
bool isDelayed() const { return m_isDelayed; }
|
||||
OrderVarVertex* pilNewVertexp() const { return m_pilNewVertexp; }
|
||||
void pilNewVertexp (OrderVarVertex* vertexp) { m_pilNewVertexp = vertexp; }
|
||||
};
|
||||
|
||||
class OrderVarStdVertex : public OrderVarVertex {
|
||||
OrderVarStdVertex(V3Graph* graphp, const OrderVarStdVertex& old)
|
||||
: OrderVarVertex(graphp, old) {}
|
||||
public:
|
||||
OrderVarStdVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: OrderVarVertex(graphp, scopep,varScp) {}
|
||||
: OrderVarVertex(graphp, scopep, varScp) {}
|
||||
virtual ~OrderVarStdVertex() {}
|
||||
virtual OrderVarStdVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderVarStdVertex(graphp, *this); }
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARSTD; }
|
||||
virtual OrderVarVertex* clone (V3Graph* graphp) const {
|
||||
return new OrderVarStdVertex(graphp, scopep(), varScp());
|
||||
}
|
||||
virtual string name() const { return (cvtToStr((void*)varScp())+"\\n "+varScp()->name());}
|
||||
virtual string dotColor() const { return "skyblue"; }
|
||||
virtual bool domainMatters() { return true; }
|
||||
};
|
||||
class OrderVarPreVertex : public OrderVarVertex {
|
||||
OrderVarPreVertex(V3Graph* graphp, const OrderVarPreVertex& old)
|
||||
: OrderVarVertex(graphp, old) {}
|
||||
public:
|
||||
OrderVarPreVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: OrderVarVertex(graphp, scopep,varScp) {}
|
||||
virtual ~OrderVarPreVertex() {}
|
||||
virtual OrderVarPreVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderVarPreVertex(graphp, *this); }
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARPRE; }
|
||||
virtual OrderVarVertex* clone (V3Graph* graphp) const {
|
||||
return new OrderVarPreVertex(graphp, scopep(), varScp());
|
||||
}
|
||||
virtual string name() const { return (cvtToStr((void*)varScp())+" PRE\\n "+varScp()->name());}
|
||||
virtual string dotColor() const { return "lightblue"; }
|
||||
virtual bool domainMatters() { return false; }
|
||||
};
|
||||
class OrderVarPostVertex : public OrderVarVertex {
|
||||
OrderVarPostVertex(V3Graph* graphp, const OrderVarPostVertex& old)
|
||||
: OrderVarVertex(graphp, old) {}
|
||||
public:
|
||||
OrderVarPostVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: OrderVarVertex(graphp, scopep,varScp) {}
|
||||
virtual OrderVarPostVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderVarPostVertex(graphp, *this); }
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARPOST; }
|
||||
virtual ~OrderVarPostVertex() {}
|
||||
virtual OrderVarVertex* clone (V3Graph* graphp) const {
|
||||
return new OrderVarPostVertex(graphp, scopep(), varScp());
|
||||
}
|
||||
virtual string name() const { return (cvtToStr((void*)varScp())+" POST\\n "+varScp()->name());}
|
||||
virtual string dotColor() const { return "CadetBlue"; }
|
||||
virtual bool domainMatters() { return false; }
|
||||
};
|
||||
class OrderVarPordVertex : public OrderVarVertex {
|
||||
OrderVarPordVertex(V3Graph* graphp, const OrderVarPordVertex& old)
|
||||
: OrderVarVertex(graphp, old) {}
|
||||
public:
|
||||
OrderVarPordVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: OrderVarVertex(graphp, scopep,varScp) {}
|
||||
virtual OrderVarVertex* clone (V3Graph* graphp) const {
|
||||
return new OrderVarPordVertex(graphp, scopep(), varScp());
|
||||
}
|
||||
virtual ~OrderVarPordVertex() {}
|
||||
virtual OrderVarPordVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderVarPordVertex(graphp, *this); }
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARPORD; }
|
||||
virtual string name() const { return (cvtToStr((void*)varScp())+" PORD\\n "+varScp()->name());}
|
||||
virtual string dotColor() const { return "NavyBlue"; }
|
||||
virtual bool domainMatters() { return false; }
|
||||
};
|
||||
class OrderVarSettleVertex : public OrderVarVertex {
|
||||
OrderVarSettleVertex(V3Graph* graphp, const OrderVarSettleVertex& old)
|
||||
: OrderVarVertex(graphp, old) {}
|
||||
public:
|
||||
OrderVarSettleVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: OrderVarVertex(graphp, scopep,varScp) {}
|
||||
virtual ~OrderVarSettleVertex() {}
|
||||
virtual OrderVarSettleVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderVarSettleVertex(graphp, *this); }
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARSETTLE; }
|
||||
virtual OrderVarVertex* clone (V3Graph* graphp) const {
|
||||
return new OrderVarSettleVertex(graphp, scopep(), varScp());
|
||||
}
|
||||
virtual string name() const { return (cvtToStr((void*)varScp())+" STL\\n "+varScp()->name());}
|
||||
virtual string dotColor() const { return "PowderBlue"; }
|
||||
virtual bool domainMatters() { return false; }
|
||||
@@ -288,13 +325,18 @@ class OrderLoopBeginVertex : public OrderLogicVertex {
|
||||
// However, a LoopBeginVertex is not "under" the loop per se, and it may be under another loop.
|
||||
OrderLoopId m_loopId; // Arbitrary # to ID this loop
|
||||
uint32_t m_loopColor; // Color # of loop (for debug)
|
||||
OrderLoopBeginVertex(V3Graph* graphp, const OrderLoopBeginVertex& old)
|
||||
: OrderLogicVertex(graphp, *this)
|
||||
, m_loopId(old.m_loopId), m_loopColor(old.m_loopColor) {}
|
||||
public:
|
||||
OrderLoopBeginVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp, AstUntilStable* nodep,
|
||||
OrderLoopId loopId, uint32_t loopColor)
|
||||
: OrderLogicVertex(graphp, scopep, domainp, nodep)
|
||||
, m_loopId(loopId), m_loopColor(loopColor) {
|
||||
}
|
||||
, m_loopId(loopId), m_loopColor(loopColor) {}
|
||||
virtual ~OrderLoopBeginVertex() {}
|
||||
virtual OrderLoopBeginVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderLoopBeginVertex(graphp, *this);
|
||||
}
|
||||
// Methods
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_LOOPBEGIN; }
|
||||
virtual string name() const { return "LoopBegin_"+cvtToStr(loopId())+"_c"+cvtToStr(loopColor()); }
|
||||
@@ -306,17 +348,22 @@ public:
|
||||
};
|
||||
|
||||
class OrderLoopEndVertex : public OrderLogicVertex {
|
||||
// A end vertex points to the *same nodep* as the Begin,
|
||||
// as we need it to be a logic vertex for moving, but don't need a permanent node.
|
||||
// We won't add to the output graph though, so it shouldn't matter.
|
||||
// A end vertex points to the *same nodep* as the Begin, as we need it to
|
||||
// be a logic vertex for moving, but don't need a permanent node. We
|
||||
// won't add to the output graph though, so it shouldn't matter.
|
||||
OrderLoopBeginVertex* m_beginVertexp; // Corresponding loop begin
|
||||
OrderLoopEndVertex(V3Graph* graphp, const OrderLoopEndVertex& old)
|
||||
: OrderLogicVertex(graphp, old), m_beginVertexp(old.m_beginVertexp) {}
|
||||
public:
|
||||
OrderLoopEndVertex(V3Graph* graphp, OrderLoopBeginVertex* beginVertexp)
|
||||
: OrderLogicVertex(graphp, beginVertexp->scopep(), beginVertexp->domainp(), beginVertexp->nodep())
|
||||
: OrderLogicVertex(graphp, beginVertexp->scopep(),
|
||||
beginVertexp->domainp(), beginVertexp->nodep())
|
||||
, m_beginVertexp(beginVertexp) {
|
||||
inLoop(beginVertexp->loopId());
|
||||
}
|
||||
virtual ~OrderLoopEndVertex() {}
|
||||
virtual OrderLoopEndVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderLoopEndVertex(graphp, *this); }
|
||||
// Methods
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_LOOPEND; }
|
||||
virtual string name() const { return "LoopEnd_"+cvtToStr(inLoop())+"_c"+cvtToStr(loopColor()); }
|
||||
@@ -342,10 +389,17 @@ protected:
|
||||
// for the head of the list, see the same var name under OrderVisitor
|
||||
V3ListEnt<OrderMoveVertex*> m_pomWaitingE; // List of nodes needing inputs to become ready
|
||||
V3ListEnt<OrderMoveVertex*> m_readyVerticesE;// List of ready under domain/scope
|
||||
// CONSTRUCTORS
|
||||
OrderMoveVertex(V3Graph* graphp, const OrderMoveVertex& old)
|
||||
: V3GraphVertex(graphp, old), m_logicp(old.m_logicp), m_state(old.m_state)
|
||||
, m_domScopep(old.m_domScopep) {}
|
||||
public:
|
||||
OrderMoveVertex(V3Graph* graphp, OrderLogicVertex* logicp)
|
||||
: V3GraphVertex(graphp), m_logicp(logicp), m_state(POM_WAIT), m_domScopep(NULL) {}
|
||||
virtual ~OrderMoveVertex() {}
|
||||
virtual OrderMoveVertex* clone(V3Graph* graphp) const {
|
||||
return new OrderMoveVertex(graphp, *this);
|
||||
}
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_MOVE; }
|
||||
virtual string dotColor() const { return logicp()->dotColor(); }
|
||||
virtual string name() const {
|
||||
@@ -376,18 +430,22 @@ public:
|
||||
// Edge types
|
||||
|
||||
class OrderEdge : public V3GraphEdge {
|
||||
protected:
|
||||
OrderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
|
||||
const OrderEdge& old)
|
||||
: V3GraphEdge(graphp, fromp, top, old) {}
|
||||
public:
|
||||
OrderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
|
||||
int weight, bool cutable=false)
|
||||
: V3GraphEdge(graphp, fromp, top, weight, cutable) {}
|
||||
virtual ~OrderEdge() {}
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_STD; }
|
||||
virtual OrderEdge* clone (V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new OrderEdge(graphp, fromp, top, *this);
|
||||
}
|
||||
// When ordering combo blocks with stronglyConnected, follow edges not involving pre/pos variables
|
||||
virtual bool followComboConnected() const { return true; }
|
||||
virtual bool followSequentConnected() const { return true; }
|
||||
virtual OrderEdge* clone (V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new OrderEdge(graphp, fromp, top, weight(), cutable());
|
||||
}
|
||||
static bool followComboConnected(const V3GraphEdge* edgep) {
|
||||
const OrderEdge* oedgep = dynamic_cast<const OrderEdge*>(edgep);
|
||||
if (!oedgep) v3fatalSrc("Following edge of non-OrderEdge type");
|
||||
@@ -403,14 +461,17 @@ public:
|
||||
class OrderChangeDetEdge : public OrderEdge {
|
||||
// Edge created from variable to OrderLoopEndVertex
|
||||
// Indicates a change detect will be required for this loop construct
|
||||
OrderChangeDetEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
|
||||
const OrderChangeDetEdge& old)
|
||||
: OrderEdge(graphp, fromp, top, old) {}
|
||||
public:
|
||||
OrderChangeDetEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
|
||||
: OrderEdge(graphp, fromp, top, WEIGHT_MEDIUM, false) {}
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_CHANGEDET; }
|
||||
virtual OrderEdge* clone (V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new OrderChangeDetEdge(graphp, fromp, top);
|
||||
}
|
||||
virtual ~OrderChangeDetEdge() {}
|
||||
virtual OrderChangeDetEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new OrderChangeDetEdge(graphp, fromp, top, *this);
|
||||
}
|
||||
virtual string dotColor() const { return "blue"; }
|
||||
};
|
||||
|
||||
@@ -418,14 +479,17 @@ class OrderComboCutEdge : public OrderEdge {
|
||||
// Edge created from output of combo logic
|
||||
// Breakable if the output var is also a input,
|
||||
// in which case we'll need a change detect loop around this var.
|
||||
OrderComboCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
|
||||
const OrderComboCutEdge& old)
|
||||
: OrderEdge(graphp, fromp, top, old) {}
|
||||
public:
|
||||
OrderComboCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
|
||||
: OrderEdge(graphp, fromp, top, WEIGHT_COMBO, CUTABLE) {}
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_COMBOCUT; }
|
||||
virtual OrderEdge* clone (V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new OrderComboCutEdge(graphp, fromp, top);
|
||||
}
|
||||
virtual ~OrderComboCutEdge() {}
|
||||
virtual OrderComboCutEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new OrderComboCutEdge(graphp, fromp, top, *this);
|
||||
}
|
||||
virtual string dotColor() const { return "yellowGreen"; }
|
||||
virtual bool followComboConnected() const { return true; }
|
||||
virtual bool followSequentConnected() const { return true; }
|
||||
@@ -435,14 +499,17 @@ class OrderPostCutEdge : public OrderEdge {
|
||||
// Edge created from output of post assignment
|
||||
// Breakable if the output var feeds back to input combo logic or another clock pin
|
||||
// in which case we'll need a change detect loop around this var.
|
||||
OrderPostCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
|
||||
const OrderPostCutEdge& old)
|
||||
: OrderEdge(graphp, fromp, top, old) {}
|
||||
public:
|
||||
OrderPostCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
|
||||
: OrderEdge(graphp, fromp, top, WEIGHT_COMBO, CUTABLE) {}
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_POSTCUT; }
|
||||
virtual OrderEdge* clone (V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new OrderPostCutEdge(graphp, fromp, top);
|
||||
}
|
||||
virtual ~OrderPostCutEdge() {}
|
||||
virtual OrderPostCutEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new OrderPostCutEdge(graphp, fromp, top, *this);
|
||||
}
|
||||
virtual string dotColor() const { return "PaleGreen"; }
|
||||
virtual bool followComboConnected() const { return false; }
|
||||
virtual bool followSequentConnected() const { return true; }
|
||||
@@ -452,16 +519,18 @@ class OrderPreCutEdge : public OrderEdge {
|
||||
// Edge created from var_PREVAR->consuming logic vertex
|
||||
// Always breakable, just results in performance loss
|
||||
// in which case we can't optimize away the pre/post delayed assignments
|
||||
OrderPreCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top,
|
||||
const OrderPreCutEdge& old)
|
||||
: OrderEdge(graphp, fromp, top, old) {}
|
||||
public:
|
||||
OrderPreCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top)
|
||||
: OrderEdge(graphp, fromp, top, WEIGHT_PRE, CUTABLE) {}
|
||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_PRECUT; }
|
||||
virtual OrderEdge* clone (V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new OrderPreCutEdge(graphp, fromp, top);
|
||||
virtual OrderPreCutEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const {
|
||||
return new OrderPreCutEdge(graphp, fromp, top, *this);
|
||||
}
|
||||
virtual ~OrderPreCutEdge() {}
|
||||
virtual string dotColor() const { return "khaki"; }
|
||||
virtual bool followComboConnected() const { return false; }
|
||||
virtual bool followSequentConnected() const { return false; }
|
||||
};
|
||||
|
||||
|
||||
+123
-18
@@ -21,13 +21,31 @@
|
||||
// Top down traversal:
|
||||
// For each cell:
|
||||
// If parameterized,
|
||||
// Determine all parameter widths, constant values
|
||||
// Determine all parameter widths, constant values.
|
||||
// (Interfaces also matter, as if an interface is parameterized
|
||||
// this effectively changes the width behavior of all that
|
||||
// reference the iface.)
|
||||
// Clone module cell calls, renaming with __{par1}_{par2}_...
|
||||
// Substitute constants for cell's module's parameters
|
||||
// Relink pins and cell to point to new module
|
||||
// Then process all modules called by that cell
|
||||
// Substitute constants for cell's module's parameters.
|
||||
// Relink pins and cell and ifacerefdtype to point to new module.
|
||||
//
|
||||
// For interface Parent's we have the AstIfaceRefDType::cellp()
|
||||
// pointing to this module. If that parent cell's interface
|
||||
// module gets parameterized, AstIfaceRefDType::cloneRelink
|
||||
// will update AstIfaceRefDType::cellp(), and AstLinkDot will
|
||||
// see the new interface.
|
||||
//
|
||||
// However if a submodule's AstIfaceRefDType::ifacep() points
|
||||
// to the old (unparameterized) interface and needs correction.
|
||||
// To detect this we must walk all pins looking for interfaces
|
||||
// that the parent has changed and propagate down.
|
||||
//
|
||||
// Then process all modules called by that cell.
|
||||
// (Cells never referenced after parameters expanded must be ignored.)
|
||||
//
|
||||
// After we complete parameters, the varp's will be wrong (point to old module)
|
||||
// and must be relinked.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
@@ -62,23 +80,34 @@ private:
|
||||
AstUser5InUse m_inuser5;
|
||||
// User1/2/3 used by constant function simulations
|
||||
|
||||
// TYPES
|
||||
typedef deque<pair<AstIfaceRefDType*,AstIfaceRefDType*> > IfaceRefRefs; // Note may have duplicate entries
|
||||
|
||||
// STATE
|
||||
typedef std::map<AstVar*,AstVar*> VarCloneMap;
|
||||
typedef map<AstVar*,AstVar*> VarCloneMap;
|
||||
struct ModInfo {
|
||||
AstNodeModule* m_modp; // Module with specified name
|
||||
VarCloneMap m_cloneMap; // Map of old-varp -> new cloned varp
|
||||
ModInfo(AstNodeModule* modp) { m_modp=modp; }
|
||||
};
|
||||
typedef std::map<string,ModInfo> ModNameMap;
|
||||
typedef map<string,ModInfo> ModNameMap;
|
||||
ModNameMap m_modNameMap; // Hash of created module flavors by name
|
||||
|
||||
typedef std::map<string,string> LongMap;
|
||||
typedef map<string,string> LongMap;
|
||||
LongMap m_longMap; // Hash of very long names to unique identity number
|
||||
int m_longId;
|
||||
|
||||
typedef map<AstNode*,int> ValueMap;
|
||||
typedef map<int,int> NextValueMap;
|
||||
ValueMap m_valueMap; // Hash of node to param value
|
||||
NextValueMap m_nextValueMap;// Hash of param value to next value to be used
|
||||
|
||||
typedef multimap<int,AstNodeModule*> LevelModMap;
|
||||
LevelModMap m_todoModps; // Modules left to process
|
||||
|
||||
typedef deque<AstCell*> CellList;
|
||||
CellList m_cellps; // Cells left to process (in this module)
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
@@ -91,7 +120,7 @@ private:
|
||||
// Pass 1, assign first letter to each gparam's name
|
||||
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
|
||||
if (AstVar* varp = stmtp->castVar()) {
|
||||
if (varp->isGParam()) {
|
||||
if (varp->isGParam()||varp->isIfaceRef()) {
|
||||
char ch = varp->name()[0];
|
||||
ch = toupper(ch); if (ch<'A' || ch>'Z') ch='Z';
|
||||
varp->user4(usedLetter[static_cast<int>(ch)]*256 + ch);
|
||||
@@ -113,6 +142,27 @@ private:
|
||||
}
|
||||
return st;
|
||||
}
|
||||
string paramValueNumber(AstNode* nodep) {
|
||||
// Given a compilcated object create a number to use for param module assignment
|
||||
// Ideally would be relatively stable if design changes (not use pointer value),
|
||||
// and must return same value given same input node
|
||||
// Return must presently be numberic so doesn't collide with 'small' alphanumeric parameter names
|
||||
ValueMap::iterator it = m_valueMap.find(nodep);
|
||||
if (it != m_valueMap.end()) {
|
||||
return cvtToStr(it->second);
|
||||
} else {
|
||||
static int BUCKETS = 1000;
|
||||
V3Hash hash (nodep->name());
|
||||
int bucket = hash.hshval() % BUCKETS;
|
||||
int offset = 0;
|
||||
NextValueMap::iterator it = m_nextValueMap.find(bucket);
|
||||
if (it != m_nextValueMap.end()) { offset = it->second; it->second = offset + 1; }
|
||||
else { m_nextValueMap.insert(make_pair(bucket, offset + 1)); }
|
||||
int num = bucket + offset * BUCKETS;
|
||||
m_valueMap.insert(make_pair(nodep, num));
|
||||
return cvtToStr(num);
|
||||
}
|
||||
}
|
||||
void relinkPins(VarCloneMap* clonemapp, AstPin* startpinp) {
|
||||
for (AstPin* pinp = startpinp; pinp; pinp=pinp->nextp()->castPin()) {
|
||||
if (!pinp->modVarp()) pinp->v3fatalSrc("Not linked?\n");
|
||||
@@ -123,9 +173,10 @@ private:
|
||||
pinp->modVarp(cloneiter->second);
|
||||
}
|
||||
}
|
||||
void visitCell(AstCell* nodep);
|
||||
void visitModules() {
|
||||
// Loop on all modules left to process
|
||||
// Hitting a cell adds to the appropriate leval of this level-sorted list,
|
||||
// Hitting a cell adds to the appropriate level of this level-sorted list,
|
||||
// so since cells originally exist top->bottom we process in top->bottom order too.
|
||||
while (!m_todoModps.empty()) {
|
||||
LevelModMap::iterator it = m_todoModps.begin();
|
||||
@@ -134,7 +185,19 @@ private:
|
||||
if (!nodep->user5SetOnce()) { // Process once; note clone() must clear so we do it again
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
// Note this may add to m_todoModps
|
||||
// Note above iterate may add to m_todoModps
|
||||
//
|
||||
// Process interface cells, then non-interface which may ref an interface cell
|
||||
for (int nonIf=0; nonIf<2; ++nonIf) {
|
||||
for (CellList::iterator it=m_cellps.begin(); it!=m_cellps.end(); ++it) {
|
||||
AstCell* nodep = *it;
|
||||
if ((nonIf==0 && nodep->modp()->castIface())
|
||||
|| (nonIf==1 && !nodep->modp()->castIface())) {
|
||||
visitCell(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_cellps.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,7 +220,10 @@ private:
|
||||
UINFO(4," MOD-dead? "<<nodep<<endl); // Should have been done by now, if not dead
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep, AstNUser*);
|
||||
virtual void visit(AstCell* nodep, AstNUser*) {
|
||||
// Must do ifaces first, so push to list and do in proper order
|
||||
m_cellps.push_back(nodep);
|
||||
}
|
||||
|
||||
// Make sure all parameters are constantified
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
@@ -218,7 +284,7 @@ private:
|
||||
//! Parameter subsitution for generated for loops.
|
||||
//! @todo Unlike generated IF, we don't have to worry about short-circuiting the conditional
|
||||
//! expression, since this is currently restricted to simple comparisons. If we ever do
|
||||
//! move to more generic constant expressions, such code will be neede here.
|
||||
//! move to more generic constant expressions, such code will be needed here.
|
||||
virtual void visit(AstBegin* nodep, AstNUser*) {
|
||||
if (nodep->genforp()) {
|
||||
AstGenFor* forp = nodep->genforp()->castGenFor();
|
||||
@@ -317,11 +383,13 @@ public:
|
||||
//----------------------------------------------------------------------
|
||||
// VISITs
|
||||
|
||||
void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
|
||||
void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
// Cell: Check for parameters in the instantiation.
|
||||
nodep->iterateChildren(*this);
|
||||
if (!nodep->modp()) { nodep->dumpTree(cerr,"error:"); nodep->v3fatalSrc("Not linked?"); }
|
||||
if (nodep->paramsp()) {
|
||||
if (!nodep->modp()) nodep->v3fatalSrc("Not linked?");
|
||||
if (nodep->paramsp()
|
||||
|| 1 // Need to look for interfaces; could track when one exists, but should be harmless to always do this
|
||||
) {
|
||||
UINFO(4,"De-parameterize: "<<nodep<<endl);
|
||||
// Create new module name with _'s between the constants
|
||||
if (debug()>=10) nodep->dumpTree(cout,"-cell:\t");
|
||||
@@ -336,7 +404,6 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
|
||||
longname += "_";
|
||||
if (debug()>8) nodep->paramsp()->dumpTreeAndNext(cout,"-cellparams:\t");
|
||||
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
if (!pinp) nodep->v3fatalSrc("Non pin under cell params\n");
|
||||
if (!pinp->exprp()) continue; // No-connect
|
||||
AstVar* modvarp = pinp->modVarp();
|
||||
if (!modvarp) {
|
||||
@@ -360,6 +427,30 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
|
||||
}
|
||||
}
|
||||
}
|
||||
IfaceRefRefs ifaceRefRefs;
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
AstVar* modvarp = pinp->modVarp();
|
||||
if (modvarp->isIfaceRef()) {
|
||||
AstIfaceRefDType* portIrefp = modvarp->subDTypep()->castIfaceRefDType();
|
||||
//UINFO(9," portIfaceRef "<<portIrefp<<endl);
|
||||
if (!pinp->exprp()
|
||||
|| !pinp->exprp()->castVarRef()
|
||||
|| !pinp->exprp()->castVarRef()->varp()
|
||||
|| !pinp->exprp()->castVarRef()->varp()->subDTypep()
|
||||
|| !pinp->exprp()->castVarRef()->varp()->subDTypep()->castIfaceRefDType()) {
|
||||
pinp->v3error("Interface port '"<<modvarp->prettyName()<<"' is not connected to interface/modport pin expression");
|
||||
} else {
|
||||
AstIfaceRefDType* pinIrefp = pinp->exprp()->castVarRef()->varp()->subDTypep()->castIfaceRefDType();
|
||||
//UINFO(9," pinIfaceRef "<<pinIrefp<<endl);
|
||||
if (portIrefp->ifaceViaCellp() != pinIrefp->ifaceViaCellp()) {
|
||||
UINFO(9," IfaceRefDType needs reconnect "<<pinIrefp<<endl);
|
||||
longname += "_" + paramSmallName(nodep->modp(),pinp->modVarp())+paramValueNumber(pinIrefp);
|
||||
any_overrides = true;
|
||||
ifaceRefRefs.push_back(make_pair(portIrefp,pinIrefp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!any_overrides) {
|
||||
UINFO(8,"Cell parameters all match original values, skipping expansion.\n");
|
||||
@@ -402,7 +493,7 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
|
||||
// Note we allow multiple users of a parameterized model, thus we need to stash this info.
|
||||
for (AstNode* stmtp=modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (AstVar* varp = stmtp->castVar()) {
|
||||
if (varp->isIO() || varp->isGParam()) {
|
||||
if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) {
|
||||
// Cloning saved a pointer to the new node for us, so just follow that link.
|
||||
AstVar* oldvarp = varp->clonep()->castVar();
|
||||
//UINFO(8,"Clone list 0x"<<hex<<(uint32_t)oldvarp<<" -> 0x"<<(uint32_t)varp<<endl);
|
||||
@@ -414,7 +505,21 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
|
||||
// Relink parameter vars to the new module
|
||||
relinkPins(clonemapp, nodep->paramsp());
|
||||
|
||||
// Fix any interface references
|
||||
for (IfaceRefRefs::iterator it=ifaceRefRefs.begin(); it!=ifaceRefRefs.end(); ++it) {
|
||||
AstIfaceRefDType* portIrefp = it->first;
|
||||
AstIfaceRefDType* pinIrefp = it->second;
|
||||
AstIfaceRefDType* cloneIrefp = portIrefp->clonep()->castIfaceRefDType();
|
||||
UINFO(8," IfaceOld "<<portIrefp<<endl);
|
||||
UINFO(8," IfaceTo "<<pinIrefp<<endl);
|
||||
if (!cloneIrefp) portIrefp->v3fatalSrc("parameter clone didn't hit AstIfaceRefDType");
|
||||
UINFO(8," IfaceClo "<<cloneIrefp<<endl);
|
||||
cloneIrefp->ifacep(pinIrefp->ifaceViaCellp());
|
||||
UINFO(8," IfaceNew "<<cloneIrefp<<endl);
|
||||
}
|
||||
|
||||
// Assign parameters to the constants specified
|
||||
// DOES clone() so must be finished with module clonep() before here
|
||||
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
AstVar* modvarp = pinp->modVarp();
|
||||
if (modvarp && pinp->exprp()) {
|
||||
@@ -440,7 +545,7 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
|
||||
} // if any_overrides
|
||||
|
||||
// Delete the parameters from the cell; they're not relevant any longer.
|
||||
nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
UINFO(8," Done with "<<nodep<<endl);
|
||||
//if (debug()>=10) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param-out.tree"));
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ struct V3ParseBisonYYSType {
|
||||
AstCell* cellp;
|
||||
AstConst* constp;
|
||||
AstMemberDType* memberp;
|
||||
AstModportVarRef* modportvarrefp;
|
||||
AstNodeModule* modulep;
|
||||
AstNodeClassDType* classp;
|
||||
AstNodeDType* dtypep;
|
||||
@@ -77,6 +78,7 @@ struct V3ParseBisonYYSType {
|
||||
AstPackageRef* packagerefp;
|
||||
AstParseRef* parserefp;
|
||||
AstPatMember* patmemberp;
|
||||
AstPattern* patternp;
|
||||
AstPin* pinp;
|
||||
AstRange* rangep;
|
||||
AstSenTree* sentreep;
|
||||
|
||||
+1
-1
@@ -128,7 +128,7 @@ public:
|
||||
}
|
||||
// Walk old sym table and reinsert into current table
|
||||
// We let V3LinkDot report the error instead of us
|
||||
symCurrentp()->import(&m_syms, symp, id_or_star);
|
||||
symCurrentp()->importFromPackage(&m_syms, symp, id_or_star);
|
||||
}
|
||||
public:
|
||||
// CREATORS
|
||||
|
||||
+2
-1
@@ -107,7 +107,8 @@ public:
|
||||
//*************************************************************************
|
||||
// Data for a preprocessor instantiation.
|
||||
|
||||
struct V3PreProcImp : public V3PreProc {
|
||||
class V3PreProcImp : public V3PreProc {
|
||||
public:
|
||||
// TYPES
|
||||
typedef std::map<string,V3Define> DefinesMap;
|
||||
typedef V3InFilter::StrList StrList;
|
||||
|
||||
+22
-6
@@ -150,6 +150,14 @@ private:
|
||||
m_scopep->addActivep(clonep);
|
||||
clonep->iterateChildren(*this); // We iterate under the *clone*
|
||||
}
|
||||
virtual void visit(AstAssignVarScope* nodep, AstNUser*) {
|
||||
// Copy under the scope but don't recurse
|
||||
UINFO(4," Move "<<nodep<<endl);
|
||||
AstNode* clonep = nodep->cloneTree(false);
|
||||
nodep->user2p(clonep);
|
||||
m_scopep->addActivep(clonep);
|
||||
clonep->iterateChildren(*this); // We iterate under the *clone*
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
||||
// Add to list of blocks under this scope
|
||||
UINFO(4," Move "<<nodep<<endl);
|
||||
@@ -215,12 +223,17 @@ private:
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
// VarRef needs to point to VarScope
|
||||
// Make sure variable has made user1p.
|
||||
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);
|
||||
if (!nodep->varp()) nodep->v3fatalSrc("Unlinked");
|
||||
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);
|
||||
}
|
||||
}
|
||||
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()
|
||||
@@ -298,6 +311,9 @@ private:
|
||||
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
|
||||
movedDeleteOrIterate(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignVarScope* nodep, AstNUser*) {
|
||||
movedDeleteOrIterate(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
||||
movedDeleteOrIterate(nodep);
|
||||
}
|
||||
|
||||
+3
-3
@@ -431,11 +431,11 @@ private:
|
||||
if (jumpingOver(nodep)) return;
|
||||
if (!optimizable()) return; // Accelerate
|
||||
if (nodep->castAssignDly()) {
|
||||
if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non dly assigns");
|
||||
if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non-dly assigns");
|
||||
m_anyAssignDly = true;
|
||||
m_inDlyAssign = true;
|
||||
} else {
|
||||
if (m_anyAssignDly) clearOptimizable(nodep, "Mix of dly/non dly assigns");
|
||||
if (m_anyAssignDly) clearOptimizable(nodep, "Mix of dly/non-dly assigns");
|
||||
m_anyAssignComb = true;
|
||||
}
|
||||
if (!nodep->lhsp()->castVarRef()) {
|
||||
@@ -607,7 +607,7 @@ private:
|
||||
V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
|
||||
for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) {
|
||||
AstVar* portp = it->first;
|
||||
AstNode* pinp = it->second;
|
||||
AstNode* pinp = it->second->exprp();
|
||||
if (pinp==NULL) {
|
||||
// Too few arguments in function call - ignore it
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -416,7 +416,7 @@ private:
|
||||
}
|
||||
if (splitAlwaysp) {
|
||||
++m_statSplits;
|
||||
AstAlways* alwaysp = new AstAlways(newListp->fileline(), NULL, NULL);
|
||||
AstAlways* alwaysp = new AstAlways(newListp->fileline(), VAlwaysKwd::ALWAYS, NULL, NULL);
|
||||
addAfterp->addNextHere(alwaysp); addAfterp=alwaysp;
|
||||
alwaysp->addStmtp(newListp);
|
||||
} else {
|
||||
|
||||
+17
-5
@@ -137,11 +137,11 @@ public:
|
||||
VSymEnt* findIdFlat(const string& name) const {
|
||||
// Find identifier without looking upward through symbol hierarchy
|
||||
// First, scan this begin/end block or module for the name
|
||||
IdNameMap::const_iterator iter = m_idNameMap.find(name);
|
||||
IdNameMap::const_iterator it = m_idNameMap.find(name);
|
||||
UINFO(9, " SymFind se"<<(void*)this<<" '"<<name
|
||||
<<"' -> "<<(iter == m_idNameMap.end() ? "NONE"
|
||||
: "se"+cvtToStr((void*)(iter->second))+" n="+cvtToStr((void*)(iter->second->nodep())))<<endl);
|
||||
if (iter != m_idNameMap.end()) return (iter->second);
|
||||
<<"' -> "<<(it == m_idNameMap.end() ? "NONE"
|
||||
: "se"+cvtToStr((void*)(it->second))+" n="+cvtToStr((void*)(it->second->nodep())))<<endl);
|
||||
if (it != m_idNameMap.end()) return (it->second);
|
||||
return NULL;
|
||||
}
|
||||
VSymEnt* findIdFallback(const string& name) const {
|
||||
@@ -166,7 +166,7 @@ private:
|
||||
}
|
||||
}
|
||||
public:
|
||||
bool import(VSymGraph* graphp, const VSymEnt* srcp, const string& id_or_star) {
|
||||
bool importFromPackage(VSymGraph* graphp, const VSymEnt* srcp, const string& id_or_star) {
|
||||
// Import tokens from source symbol table into this symbol table
|
||||
// Returns true if successful
|
||||
bool any = false;
|
||||
@@ -183,6 +183,18 @@ public:
|
||||
}
|
||||
return any;
|
||||
}
|
||||
void importFromIface(VSymGraph* graphp, const VSymEnt* srcp) {
|
||||
// Import interface tokens from source symbol table into this symbol table, recursively
|
||||
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);
|
||||
// And recurse to create children
|
||||
srcp->importFromIface(graphp, symp);
|
||||
}
|
||||
}
|
||||
void cellErrorScopes(AstNode* lookp, string prettyName="") {
|
||||
if (prettyName=="") prettyName = lookp->prettyName();
|
||||
string scopes;
|
||||
|
||||
+90
-24
@@ -370,7 +370,8 @@ private:
|
||||
V3TaskConnects tconnects = V3Task::taskConnects(refp, beginp);
|
||||
for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) {
|
||||
AstVar* portp = it->first;
|
||||
AstNode* pinp = it->second;
|
||||
AstArg* argp = it->second;
|
||||
AstNode* pinp = argp->exprp();
|
||||
portp->unlinkFrBack(); pushDeletep(portp); // Remove it from the clone (not original)
|
||||
if (pinp==NULL) {
|
||||
// Too few arguments in function call
|
||||
@@ -378,6 +379,7 @@ private:
|
||||
UINFO(9, " Port "<<portp<<endl);
|
||||
UINFO(9, " pin "<<pinp<<endl);
|
||||
pinp->unlinkFrBack(); // Relinked to assignment below
|
||||
argp->unlinkFrBack()->deleteTree(); // Args no longer needed
|
||||
//
|
||||
if ((portp->isInout()||portp->isOutput()) && pinp->castConst()) {
|
||||
pinp->v3error("Function/task output connected to constant instead of variable: "+portp->prettyName());
|
||||
@@ -476,7 +478,7 @@ private:
|
||||
V3TaskConnects tconnects = V3Task::taskConnects(refp, refp->taskp()->stmtsp());
|
||||
for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) {
|
||||
AstVar* portp = it->first;
|
||||
AstNode* pinp = it->second;
|
||||
AstNode* pinp = it->second->exprp();
|
||||
if (!pinp) {
|
||||
// Too few arguments in function call
|
||||
} else {
|
||||
@@ -532,9 +534,10 @@ private:
|
||||
AstNode* nextpinp;
|
||||
for (AstNode* pinp = refp->pinsp(); pinp; pinp=nextpinp) {
|
||||
nextpinp = pinp->nextp();
|
||||
// Move pin to the CCall
|
||||
pinp->unlinkFrBack();
|
||||
ccallp->addArgsp(pinp);
|
||||
// Move pin to the CCall, removing all Arg's
|
||||
AstNode* exprp = pinp->castArg()->exprp();
|
||||
exprp->unlinkFrBack();
|
||||
ccallp->addArgsp(exprp);
|
||||
}
|
||||
|
||||
if (outvscp) {
|
||||
@@ -1177,20 +1180,20 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp)
|
||||
// Missing pin/expr? We return (pinvar, NULL)
|
||||
// Extra pin/expr? We clean it up
|
||||
|
||||
typedef map<string,int> NameToIndex;
|
||||
NameToIndex nameToIndex;
|
||||
V3TaskConnects tconnects;
|
||||
if (!nodep->taskp()) nodep->v3fatalSrc("unlinked");
|
||||
|
||||
// Find ports
|
||||
//map<string,int> name_to_pinnum;
|
||||
int tpinnum = 0; // Note grammar starts pin counting at one
|
||||
int tpinnum = 0;
|
||||
AstVar* sformatp = NULL;
|
||||
for (AstNode* stmtp = taskStmtsp; stmtp; stmtp=stmtp->nextp()) {
|
||||
if (AstVar* portp = stmtp->castVar()) {
|
||||
if (portp->isIO()) {
|
||||
tconnects.push_back(make_pair(portp, (AstNode*)NULL));
|
||||
// Eventually we'll do name based connections
|
||||
// That'll require a AstTpin or somesuch which will replace the ppinnum counting
|
||||
//name_to_pinnum.insert(make_pair(portp->name(), tpinnum));
|
||||
tconnects.push_back(make_pair(portp, (AstArg*)NULL));
|
||||
nameToIndex.insert(make_pair(portp->name(), tpinnum)); // For name based connections
|
||||
tpinnum++;
|
||||
if (portp->attrSFormat()) {
|
||||
sformatp = portp;
|
||||
@@ -1201,27 +1204,90 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp)
|
||||
}
|
||||
}
|
||||
|
||||
// Connect pins
|
||||
// Find pins
|
||||
int ppinnum = 0;
|
||||
for (AstNode* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()) {
|
||||
if (ppinnum >= tpinnum) {
|
||||
if (sformatp) {
|
||||
tconnects.push_back(make_pair(sformatp, (AstNode*)NULL));
|
||||
bool reorganize = false;
|
||||
for (AstNode* nextp, *pinp = nodep->pinsp(); pinp; pinp=nextp) {
|
||||
nextp = pinp->nextp();
|
||||
AstArg* argp = pinp->castArg(); if (!argp) pinp->v3fatalSrc("Non-arg under ftask reference");
|
||||
if (argp->name() != "") {
|
||||
// By name
|
||||
NameToIndex::iterator it = nameToIndex.find(argp->name());
|
||||
if (it == nameToIndex.end()) {
|
||||
pinp->v3error("No such argument '"<<argp->prettyName()
|
||||
<<"' in function call to "<<nodep->taskp()->prettyTypeName());
|
||||
// We'll just delete it; seems less error prone than making a false argument
|
||||
pinp->unlinkFrBack()->deleteTree(); pinp=NULL;
|
||||
} else {
|
||||
pinp->v3error("Too many arguments in function call to "<<nodep->taskp()->prettyTypeName());
|
||||
// We'll just delete them; seems less error prone than making a false argument
|
||||
pinp->unlinkFrBackWithNext()->deleteTree(); pinp=NULL;
|
||||
break;
|
||||
if (tconnects[it->second].second) {
|
||||
pinp->v3error("Duplicate argument '"<<argp->prettyName()
|
||||
<<"' in function call to "<<nodep->taskp()->prettyTypeName());
|
||||
}
|
||||
argp->name(""); // Can forget name as will add back in pin order
|
||||
tconnects[it->second].second = argp;
|
||||
reorganize = true;
|
||||
}
|
||||
} else { // By pin number
|
||||
if (ppinnum >= tpinnum) {
|
||||
if (sformatp) {
|
||||
tconnects.push_back(make_pair(sformatp, (AstArg*)NULL));
|
||||
tconnects[ppinnum].second = argp;
|
||||
tpinnum++;
|
||||
} else {
|
||||
pinp->v3error("Too many arguments in function call to "<<nodep->taskp()->prettyTypeName());
|
||||
// We'll just delete it; seems less error prone than making a false argument
|
||||
pinp->unlinkFrBack()->deleteTree(); pinp=NULL;
|
||||
}
|
||||
} else {
|
||||
tconnects[ppinnum].second = argp;
|
||||
}
|
||||
}
|
||||
tconnects[ppinnum].second = pinp;
|
||||
ppinnum++;
|
||||
}
|
||||
|
||||
while (ppinnum < tpinnum) {
|
||||
nodep->v3error("Too few arguments in function call to "<<nodep->taskp()->prettyTypeName());
|
||||
UINFO(1,"missing argument for '"<<tconnects[ppinnum].first->prettyName()<<"'"<<endl);
|
||||
ppinnum++;
|
||||
// Connect missing ones
|
||||
for (int i=0; i<tpinnum; ++i) {
|
||||
AstVar* portp = tconnects[i].first;
|
||||
if (!tconnects[i].second || !tconnects[i].second->exprp()) {
|
||||
AstNode* newvaluep = NULL;
|
||||
if (!portp->valuep()) {
|
||||
nodep->v3error("Missing argument on non-defaulted argument '"<<portp->prettyName()
|
||||
<<"' in function call to "<<nodep->taskp()->prettyTypeName());
|
||||
newvaluep = new AstConst(nodep->fileline(), AstConst::Unsized32(), 0);
|
||||
} else if (!portp->valuep()->castConst()) {
|
||||
// Problem otherwise is we might have a varref, task call, or something else that only
|
||||
// makes sense in the domain of the function, not the callee.
|
||||
nodep->v3error("Unsupported: Non-constant default value in missing argument '"<<portp->prettyName()
|
||||
<<"' in function call to "<<nodep->taskp()->prettyTypeName());
|
||||
newvaluep = new AstConst(nodep->fileline(), AstConst::Unsized32(), 0);
|
||||
} else {
|
||||
newvaluep = portp->valuep()->cloneTree(true);
|
||||
}
|
||||
// To avoid problems with callee needing to know to deleteTree or not, we make this into a pin
|
||||
UINFO(9,"Default pin for "<<portp<<endl);
|
||||
AstArg* newp = new AstArg(nodep->fileline(), portp->name(), newvaluep);
|
||||
if (tconnects[i].second) { // Have a "NULL" pin already defined for it
|
||||
tconnects[i].second->unlinkFrBack()->deleteTree(); tconnects[i].second=NULL;
|
||||
}
|
||||
tconnects[i].second = newp;
|
||||
reorganize = true;
|
||||
}
|
||||
if (tconnects[i].second) { UINFO(9,"Connect "<<portp<<" -> "<<tconnects[i].second<<endl); }
|
||||
else { UINFO(9,"Connect "<<portp<<" -> NONE"<<endl); }
|
||||
}
|
||||
|
||||
if (reorganize) {
|
||||
// To simplify downstream, put argument list back into pure pinnumber ordering
|
||||
while (nodep->pinsp()) nodep->pinsp()->unlinkFrBack(); // Must unlink each pin, not all pins linked together as one list
|
||||
for (int i=0; i<tpinnum; ++i) {
|
||||
AstArg* argp = tconnects[i].second; if (!argp) nodep->v3fatalSrc("Lost argument in func conversion");
|
||||
nodep->addPinsp(argp);
|
||||
}
|
||||
}
|
||||
|
||||
if (debug()>=9) {
|
||||
nodep->dumpTree(cout,"-ftref-out: ");
|
||||
for (int i=0; i<tpinnum; ++i) UINFO(0," pin "<<i<<" conn="<<(void*)tconnects[i].second<<endl);
|
||||
}
|
||||
return tconnects;
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@
|
||||
|
||||
//============================================================================
|
||||
|
||||
typedef pair<AstVar*,AstNode*> V3TaskConnect; // [port, pin-connects-to]
|
||||
typedef pair<AstVar*,AstArg*> V3TaskConnect; // [port, pin-connects-to]
|
||||
typedef vector<V3TaskConnect> V3TaskConnects; // [ [port, pin-connects-to] ... ]
|
||||
|
||||
//============================================================================
|
||||
|
||||
+41
-37
@@ -20,7 +20,7 @@
|
||||
// V3Tristate's Transformations:
|
||||
//
|
||||
// Modify the design to expand tristate logic into its
|
||||
// corresponding two state reprasentation. At the lowest levels,
|
||||
// corresponding two state representation. At the lowest levels,
|
||||
//
|
||||
// In detail:
|
||||
//
|
||||
@@ -46,12 +46,15 @@
|
||||
// logic that they may go through until they hit the module level. At
|
||||
// the module level, all the output enable signals from what can be
|
||||
// many tristate drivers are combined together to produce a single
|
||||
// driver and output enable. If the signal propigates up into higher
|
||||
// driver and output enable. If the signal propagates up into higher
|
||||
// modules, then new ports are created with for the signal with
|
||||
// suffixes __en and __out. The original port is turned from an inout
|
||||
// to an input and the __out port carries the output driver signal and
|
||||
// the __en port carried the output enable for that driver.
|
||||
//
|
||||
// Note 1800-2012 adds user defined resolution functions. This suggests
|
||||
// long term this code should be scoped-based and resolve all nodes at once
|
||||
// rather than hierarchically.
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
@@ -222,6 +225,7 @@ public:
|
||||
}
|
||||
}
|
||||
m_graph.clear();
|
||||
AstNode::user5ClearTree(); // Wipe all node user5p's that point to vertexes
|
||||
}
|
||||
void graphWalk(AstNodeModule* nodep) {
|
||||
//if (debug()>=9) m_graph.dumpDotFilePrefixed("tri_pre__"+nodep->name());
|
||||
@@ -252,7 +256,7 @@ public:
|
||||
TristateVertex* vertexp = (TristateVertex*)(nodep->user5p());
|
||||
if (!vertexp) {
|
||||
// Not v3errorSrc as no reason to stop the world
|
||||
nodep->v3error("Unsupported tristate constuct (not in propagation graph): "<<nodep->prettyTypeName());
|
||||
nodep->v3error("Unsupported tristate construct (not in propagation graph): "<<nodep->prettyTypeName());
|
||||
} else {
|
||||
// We don't warn if no vertexp->isTristate() as the creation process makes midling nodes that don't have it set
|
||||
vertexp->processed(true);
|
||||
@@ -445,7 +449,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
} else {
|
||||
if (oldpullp->direction() != pullp->direction()) {
|
||||
pullp->v3error("Unsupported: Conflicting pull directions.");
|
||||
oldpullp->v3error("... Location of conflicing pull.");
|
||||
oldpullp->v3error("... Location of conflicting pull.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -494,6 +498,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
|
||||
// Now go through the lhs driver map and generate the output
|
||||
// enable logic for any tristates.
|
||||
// Note there might not be any drivers.
|
||||
for (VarMap::iterator nextit, it = m_lhsmap.begin(); it != m_lhsmap.end(); it = nextit) {
|
||||
nextit = it; ++nextit;
|
||||
AstVar* invarp = (*it).first;
|
||||
@@ -518,8 +523,8 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
// if this is the top level so that we can force the final
|
||||
// tristate resolution at the top.
|
||||
AstVar* envarp = NULL;
|
||||
AstVar* outvarp = NULL;
|
||||
AstVar* lhsp = invarp;
|
||||
AstVar* outvarp = NULL; // __out
|
||||
AstVar* lhsp = invarp; // Variable to assign drive-value to (<in> or __out)
|
||||
if (!nodep->isTop() && invarp->isIO()) {
|
||||
// This var becomes an input
|
||||
invarp->varType2In(); // convert existing port to type input
|
||||
@@ -589,12 +594,10 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
undrivenp = ((!undrivenp) ? tmp
|
||||
: new AstAnd(nodep->fileline(), tmp, undrivenp));
|
||||
}
|
||||
|
||||
if (!undrivenp) { // No drivers on the bus
|
||||
V3Number ones(nodep->fileline(), lhsp->width()); ones.setAllBits1();
|
||||
undrivenp = new AstConst(nodep->fileline(), ones);
|
||||
}
|
||||
|
||||
if (!outvarp) {
|
||||
// This is the final resolution of the tristate, so we apply
|
||||
// the pull direction to any undriven pins.
|
||||
@@ -617,7 +620,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
new AstVarRef(envarp->fileline(),
|
||||
envarp, true), enp));
|
||||
}
|
||||
|
||||
// __out (child) or <in> (parent) = drive-value expression
|
||||
AstNode* assp = new AstAssignW(lhsp->fileline(),
|
||||
new AstVarRef(lhsp->fileline(),
|
||||
lhsp,
|
||||
@@ -661,7 +664,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
AstConst* enp = new AstConst(fl, numz0);
|
||||
nodep->replaceWith(newconstp);
|
||||
pushDeletep(nodep); nodep = NULL;
|
||||
newconstp->user1p(enp);
|
||||
newconstp->user1p(enp); // propagate up constant with non-Z bits as 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -698,7 +701,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
// two expressions with the same conditional.
|
||||
AstNode* enp = new AstCond(nodep->fileline(), condp->cloneTree(false), en1p, en2p);
|
||||
UINFO(9," newcond "<<enp<<endl);
|
||||
nodep->user1p(enp);
|
||||
nodep->user1p(enp); // propagate up COND(lhsp->enable, rhsp->enable)
|
||||
expr1p->user1p(NULL);
|
||||
expr2p->user1p(NULL);
|
||||
}
|
||||
@@ -735,7 +738,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
AstNode* enp = new AstSel(nodep->fileline(), en1p,
|
||||
nodep->lsbp()->cloneTree(true), nodep->widthp()->cloneTree(true));
|
||||
UINFO(9," newsel "<<enp<<endl);
|
||||
nodep->user1p(enp);
|
||||
nodep->user1p(enp); // propagate up SEL(fromp->enable, value)
|
||||
m_tgraph.didProcess(nodep);
|
||||
}
|
||||
}
|
||||
@@ -783,7 +786,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
AstNode* en2p = getEnp(expr2p);
|
||||
AstNode* enp = new AstConcat(nodep->fileline(), en1p, en2p);
|
||||
UINFO(9," newconc "<<enp<<endl);
|
||||
nodep->user1p(enp);
|
||||
nodep->user1p(enp); // propagate up CONCAT(lhsp->enable, rhsp->enable)
|
||||
expr1p->user1p(NULL);
|
||||
expr2p->user1p(NULL);
|
||||
}
|
||||
@@ -925,14 +928,14 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
checkUnhandled(nodep);
|
||||
// Unsupported: A === 3'b000 should compare with the enables, but we don't do
|
||||
// so at present, we only compare if there is a z in the equation.
|
||||
// Otherwise we'd need to attach an enable to every signal, then optimize then
|
||||
// Otherwise we'd need to attach an enable to every signal, then optimize them
|
||||
// away later when we determine the signal has no tristate
|
||||
nodep->iterateChildren(*this);
|
||||
UINFO(9,dbgState()<<nodep<<endl);
|
||||
AstConst* constp = nodep->lhsp()->castConst(); // Constification always moves const to LHS
|
||||
AstVarRef* varrefp = nodep->rhsp()->castVarRef(); // Input variable
|
||||
if (constp && constp->user1p() && varrefp) {
|
||||
// 3'b1z0 -> ((3'b101 == __en) && (3'b100 == __in))
|
||||
// 3'b1z0 -> ((3'b101 == in__en) && (3'b100 == in))
|
||||
varrefp->unlinkFrBack();
|
||||
FileLine* fl = nodep->fileline();
|
||||
V3Number oneIfEn = constp->user1p()->castNode()->castConst()->num(); // visit(AstConst) already split into en/ones
|
||||
@@ -1021,19 +1024,20 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
// ENABLE: -> (VARREF(trisig__pinen)
|
||||
// Added complication is the signal may be an output/inout or just input with tie off (or not) up top
|
||||
// PIN PORT NEW PORTS AND CONNECTIONS
|
||||
// N/C input in(from-resolver), __out(to-resolver-only), __en(to-resolver-only)
|
||||
// N/C input in(from-resolver), __en(to-resolver-only), __out(to-resolver-only)
|
||||
// N/C inout Spec says illegal
|
||||
// N/C output Unsupported; Illegal?
|
||||
// wire input in(from-resolver-with-wire-value), __out(to-resolver-only), __en(to-resolver-only)
|
||||
// wire inout in, __out, __en
|
||||
// wire output in, __out, __en
|
||||
// const input in(from-resolver-with-const-value), __out(to-resolver-only), __en(to-resolver-only)
|
||||
// const inout Spec says illegal
|
||||
// const output Unsupported; Illegal?
|
||||
// wire input in(from-resolver-with-wire-value), __en(from-resolver-wire), __out(to-resolver-only)
|
||||
// wire inout in, __en, __out
|
||||
// wire output in, __en, __out
|
||||
// const input in(from-resolver-with-const-value), __en(from-resolver-const), __out(to-resolver-only)
|
||||
// const inout Spec says illegal
|
||||
// const output Unsupported; Illegal?
|
||||
virtual void visit(AstPin* nodep, AstNUser*) {
|
||||
if (m_graphing) {
|
||||
if (nodep->user2() & U2_GRAPHING) return; // This pin is already expanded
|
||||
nodep->user2(U2_GRAPHING);
|
||||
// Find child module's new variables.
|
||||
AstVar* enModVarp = (AstVar*) nodep->modVarp()->user1p();
|
||||
if (!enModVarp) {
|
||||
if (nodep->exprp()) {
|
||||
@@ -1048,7 +1052,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
if (debug()>=9) nodep->dumpTree(cout,"-pin-pre: ");
|
||||
|
||||
// Empty/in-only; need Z to propagate
|
||||
bool inOnlyProcessing = (nodep->exprp()
|
||||
bool inDeclProcessing = (nodep->exprp()
|
||||
&& nodep->modVarp()->isInOnly()
|
||||
// Need to consider the original state instead of current state
|
||||
// as we converted tristates to inputs, which do not want to have this.
|
||||
@@ -1060,7 +1064,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
nodep->modVarp()->isOutput()));
|
||||
m_tgraph.setTristate(ucVarp);
|
||||
// We don't need a driver on the wire; the lack of one will default to tristate
|
||||
} else if (inOnlyProcessing) { // Not an input that was a converted tristate
|
||||
} else if (inDeclProcessing) { // Not an input that was a converted tristate
|
||||
// Input only may have driver in underneath module which would stomp
|
||||
// the input value. So make a temporary connection.
|
||||
AstAssignW* reAssignp = V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp, true, true);
|
||||
@@ -1077,7 +1081,7 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
AstVar* enVarp = new AstVar(nodep->fileline(),
|
||||
AstVarType::MODULETEMP,
|
||||
nodep->name() + "__en" + cvtToStr(m_unique++),
|
||||
VFlagLogicPacked(), enModVarp->width());
|
||||
VFlagBitPacked(), enModVarp->width());
|
||||
UINFO(9," newenv "<<enVarp<<endl);
|
||||
AstPin* enpinp = new AstPin(nodep->fileline(),
|
||||
nodep->pinNum(),
|
||||
@@ -1107,9 +1111,9 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
outpinp->user2(U2_BOTH); // don't iterate the pin later
|
||||
nodep->addNextHere(outpinp);
|
||||
// Simplify
|
||||
if (inOnlyProcessing) { // Not an input that was a converted tristate
|
||||
if (inDeclProcessing) { // Not an input that was a converted tristate
|
||||
// The pin is an input, but we need an output
|
||||
// The if() above is neded because the Visitor is simple, it will flip ArraySel's and such,
|
||||
// The if() above is needed because the Visitor is simple, it will flip ArraySel's and such,
|
||||
// but if the pin is an input the earlier reconnectSimple made it a VarRef without any ArraySel, etc
|
||||
TristatePinVisitor visitor (outexprp, m_tgraph, true);
|
||||
}
|
||||
@@ -1127,29 +1131,29 @@ class TristateVisitor : public TristateBaseVisitor {
|
||||
if (debug()>=9 && inAssignp) inAssignp->dumpTree(cout,"-pin-as: ");
|
||||
|
||||
// Connect enable to output signal
|
||||
AstVarRef* outrefp;
|
||||
AstVarRef* exprrefp; // Tristate variable that the Pin's expression refers to
|
||||
if (!outAssignp) {
|
||||
outrefp = outpinp->exprp()->castVarRef();
|
||||
exprrefp = outpinp->exprp()->castVarRef();
|
||||
} else {
|
||||
outrefp = outAssignp->rhsp()->castVarRef(); // This should be the same var as the output pin
|
||||
exprrefp = outAssignp->rhsp()->castVarRef(); // This should be the same var as the output pin
|
||||
}
|
||||
if (!outrefp) { // deal with simple varref port
|
||||
if (!exprrefp) { // deal with simple varref port
|
||||
// pinReconnect should have converted this
|
||||
nodep->v3error("Unsupported tristate port expression: "<<nodep->exprp()->prettyTypeName());
|
||||
} else {
|
||||
UINFO(9,"outref "<<outrefp<<endl);
|
||||
outrefp->user1p(enrefp); // Mark as now tristated; iteration will pick it up from there
|
||||
UINFO(9,"outref "<<exprrefp<<endl);
|
||||
exprrefp->user1p(enrefp); // Mark as now tristated; iteration will pick it up from there
|
||||
if (!outAssignp) {
|
||||
mapInsertLhsVarRef(outrefp); // insertTristates will convert
|
||||
mapInsertLhsVarRef(exprrefp); // insertTristates will convert
|
||||
// // to a varref to the __out# variable
|
||||
} // else the assignment deals with the connection
|
||||
}
|
||||
|
||||
// Propagate any pullups/pulldowns upwards if necessary
|
||||
if (outrefp) {
|
||||
if (exprrefp) {
|
||||
if (AstPull* pullp = (AstPull*) nodep->modVarp()->user3p()) {
|
||||
UINFO(9, "propagate pull on "<<outrefp<<endl);
|
||||
setPullDirection(outrefp->varp(), pullp);
|
||||
UINFO(9, "propagate pull on "<<exprrefp<<endl);
|
||||
setPullDirection(exprrefp->varp(), pullp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+89
-27
@@ -133,6 +133,17 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isUsedNotDrivenBit (int bit, int width) const {
|
||||
for (int i=0; i<width; i++) {
|
||||
if (bitNumOk(bit+i)
|
||||
&& (m_usedWhole || m_flags[(bit+i)*FLAGS_PER_BIT + FLAG_USED])
|
||||
&& !(m_drivenWhole || m_flags[(bit+i)*FLAGS_PER_BIT + FLAG_DRIVEN])) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool isUsedNotDrivenAny () const {
|
||||
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;
|
||||
@@ -213,11 +224,13 @@ private:
|
||||
// NODE STATE
|
||||
// AstVar::user1p -> UndrivenVar* for usage var, 0=not set yet
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
|
||||
// STATE
|
||||
vector<UndrivenVarEntry*> m_entryps; // Nodes to delete when we are finished
|
||||
vector<UndrivenVarEntry*> m_entryps[3]; // Nodes to delete when we are finished
|
||||
bool m_markBoth; // Mark as driven+used
|
||||
AstNodeFTask* m_taskp; // Current task
|
||||
AstAlways* m_alwaysp; // Current always
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
@@ -226,31 +239,45 @@ private:
|
||||
return level;
|
||||
}
|
||||
|
||||
UndrivenVarEntry* getEntryp(AstVar* nodep) {
|
||||
if (!nodep->user1p()) {
|
||||
UndrivenVarEntry* getEntryp(AstVar* nodep, int which_user) {
|
||||
if (!(which_user==1 ? nodep->user1p() : nodep->user2p())) {
|
||||
UndrivenVarEntry* entryp = new UndrivenVarEntry (nodep);
|
||||
m_entryps.push_back(entryp);
|
||||
nodep->user1p(entryp);
|
||||
//UINFO(9," Associate u="<<which_user<<" "<<(void*)this<<" "<<nodep->name()<<endl);
|
||||
m_entryps[which_user].push_back(entryp);
|
||||
if (which_user==1) nodep->user1p(entryp);
|
||||
else if (which_user==2) nodep->user2p(entryp);
|
||||
else nodep->v3fatalSrc("Bad case");
|
||||
return entryp;
|
||||
} else {
|
||||
UndrivenVarEntry* entryp = (UndrivenVarEntry*)(nodep->user1p());
|
||||
UndrivenVarEntry* entryp = (UndrivenVarEntry*)(which_user==1 ? nodep->user1p() : nodep->user2p());
|
||||
return entryp;
|
||||
}
|
||||
}
|
||||
|
||||
void warnAlwCombOrder(AstVarRef* nodep) {
|
||||
AstVar* varp = nodep->varp();
|
||||
if (!varp->isParam() && !varp->isGenVar() && !varp->isUsedLoopIdx()
|
||||
&& !varp->fileline()->warnIsOff(V3ErrorCode::ALWCOMBORDER)) { // Warn only once per variable
|
||||
nodep->v3warn(ALWCOMBORDER, "Always_comb variable driven after use: "<<nodep->prettyName());
|
||||
varp->fileline()->modifyWarnOff(V3ErrorCode::ALWCOMBORDER, true); // Complain just once for any usage
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
UndrivenVarEntry* entryp = getEntryp (nodep);
|
||||
if (nodep->isInput()
|
||||
|| nodep->isSigPublic() || nodep->isSigUserRWPublic()
|
||||
|| (m_taskp && (m_taskp->dpiImport() || m_taskp->dpiExport()))) {
|
||||
entryp->drivenWhole();
|
||||
}
|
||||
if (nodep->isOutput()
|
||||
|| nodep->isSigPublic() || nodep->isSigUserRWPublic()
|
||||
|| nodep->isSigUserRdPublic()
|
||||
|| (m_taskp && (m_taskp->dpiImport() || m_taskp->dpiExport()))) {
|
||||
entryp->usedWhole();
|
||||
for (int usr=1; usr<(m_alwaysp?3:2); ++usr) {
|
||||
UndrivenVarEntry* entryp = getEntryp (nodep, usr);
|
||||
if (nodep->isInput()
|
||||
|| nodep->isSigPublic() || nodep->isSigUserRWPublic()
|
||||
|| (m_taskp && (m_taskp->dpiImport() || m_taskp->dpiExport()))) {
|
||||
entryp->drivenWhole();
|
||||
}
|
||||
if (nodep->isOutput()
|
||||
|| nodep->isSigPublic() || nodep->isSigUserRWPublic()
|
||||
|| nodep->isSigUserRdPublic()
|
||||
|| (m_taskp && (m_taskp->dpiImport() || m_taskp->dpiExport()))) {
|
||||
entryp->usedWhole();
|
||||
}
|
||||
}
|
||||
// Discover variables used in bit definitions, etc
|
||||
nodep->iterateChildren(*this);
|
||||
@@ -263,10 +290,19 @@ private:
|
||||
AstVarRef* varrefp = nodep->fromp()->castVarRef();
|
||||
AstConst* constp = nodep->lsbp()->castConst();
|
||||
if (varrefp && constp && !constp->num().isFourState()) {
|
||||
UndrivenVarEntry* entryp = getEntryp (varrefp->varp());
|
||||
int lsb = constp->toUInt();
|
||||
if (m_markBoth || varrefp->lvalue()) entryp->drivenBit(lsb, nodep->width());
|
||||
if (m_markBoth || !varrefp->lvalue()) entryp->usedBit(lsb, nodep->width());
|
||||
for (int usr=1; usr<(m_alwaysp?3:2); ++usr) {
|
||||
UndrivenVarEntry* entryp = getEntryp (varrefp->varp(), usr);
|
||||
int lsb = constp->toUInt();
|
||||
if (m_markBoth || varrefp->lvalue()) {
|
||||
// Don't warn if already driven earlier as "a=0; if(a) a=1;" is fine.
|
||||
if (usr==2 && m_alwaysp && entryp->isUsedNotDrivenBit(lsb, nodep->width())) {
|
||||
UINFO(9," Select. Entryp="<<(void*)entryp<<endl);
|
||||
warnAlwCombOrder(varrefp);
|
||||
}
|
||||
entryp->drivenBit(lsb, nodep->width());
|
||||
}
|
||||
if (m_markBoth || !varrefp->lvalue()) entryp->usedBit(lsb, nodep->width());
|
||||
}
|
||||
} else {
|
||||
// else other varrefs handled as unknown mess in AstVarRef
|
||||
nodep->iterateChildren(*this);
|
||||
@@ -274,10 +310,18 @@ private:
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
// Any variable
|
||||
UndrivenVarEntry* entryp = getEntryp (nodep->varp());
|
||||
bool fdrv = nodep->lvalue() && nodep->varp()->attrFileDescr(); // FD's are also being read from
|
||||
if (m_markBoth || nodep->lvalue()) entryp->drivenWhole();
|
||||
if (m_markBoth || !nodep->lvalue() || fdrv) entryp->usedWhole();
|
||||
for (int usr=1; usr<(m_alwaysp?3:2); ++usr) {
|
||||
UndrivenVarEntry* entryp = getEntryp (nodep->varp(), usr);
|
||||
bool fdrv = nodep->lvalue() && nodep->varp()->attrFileDescr(); // FD's are also being read from
|
||||
if (m_markBoth || nodep->lvalue()) {
|
||||
if (usr==2 && m_alwaysp && entryp->isUsedNotDrivenAny()) {
|
||||
UINFO(9," Full bus. Entryp="<<(void*)entryp<<endl);
|
||||
warnAlwCombOrder(nodep);
|
||||
}
|
||||
entryp->drivenWhole();
|
||||
}
|
||||
if (m_markBoth || !nodep->lvalue() || fdrv) entryp->usedWhole();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't know what black boxed calls do, assume in+out
|
||||
@@ -288,6 +332,19 @@ private:
|
||||
m_markBoth = prevMark;
|
||||
}
|
||||
|
||||
virtual void visit(AstAlways* nodep, AstNUser*) {
|
||||
AstAlways* prevAlwp = m_alwaysp;
|
||||
{
|
||||
AstNode::user2ClearTree();
|
||||
if (nodep->keyword() == VAlwaysKwd::ALWAYS_COMB) UINFO(9," "<<nodep<<endl);
|
||||
if (nodep->keyword() == VAlwaysKwd::ALWAYS_COMB) m_alwaysp = nodep;
|
||||
else m_alwaysp = NULL;
|
||||
nodep->iterateChildren(*this);
|
||||
if (nodep->keyword() == VAlwaysKwd::ALWAYS_COMB) UINFO(9," Done "<<nodep<<endl);
|
||||
}
|
||||
m_alwaysp = prevAlwp;
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
||||
AstNodeFTask* prevTaskp = m_taskp;
|
||||
m_taskp = nodep;
|
||||
@@ -315,12 +372,17 @@ public:
|
||||
UndrivenVisitor(AstNetlist* nodep) {
|
||||
m_markBoth = false;
|
||||
m_taskp = NULL;
|
||||
m_alwaysp = NULL;
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~UndrivenVisitor() {
|
||||
for (vector<UndrivenVarEntry*>::iterator it = m_entryps.begin(); it != m_entryps.end(); ++it) {
|
||||
for (vector<UndrivenVarEntry*>::iterator it = m_entryps[1].begin(); it != m_entryps[1].end(); ++it) {
|
||||
(*it)->reportViolations();
|
||||
delete (*it);
|
||||
}
|
||||
for (int usr=1; usr<3; ++usr) {
|
||||
for (vector<UndrivenVarEntry*>::iterator it = m_entryps[usr].begin(); it != m_entryps[usr].end(); ++it) {
|
||||
delete (*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ private:
|
||||
m_forVarp = initAssp->lhsp()->castVarRef()->varp();
|
||||
m_forVscp = initAssp->lhsp()->castVarRef()->varScopep();
|
||||
if (nodep->castGenFor() && !m_forVarp->isGenVar()) {
|
||||
nodep->v3error("Non-genvar used in generate for: "<<m_forVarp->name()<<endl);
|
||||
nodep->v3error("Non-genvar used in generate for: "<<m_forVarp->prettyName()<<endl);
|
||||
}
|
||||
if (m_generate) V3Const::constifyParamsEdit(initAssp->rhsp()); // rhsp may change
|
||||
AstConst* constInitp = initAssp->rhsp()->castConst();
|
||||
|
||||
+76
-33
@@ -95,7 +95,9 @@ public:
|
||||
WidthVP(int width, int widthMin, Stage stage)
|
||||
: m_width(width), m_widthMin(widthMin), m_dtypep(NULL), m_stage(stage) {}
|
||||
WidthVP(AstNodeDType* dtypep, Stage stage)
|
||||
: m_width(0), m_widthMin(0), m_dtypep(dtypep), m_stage(stage) {}
|
||||
: m_width(dtypep->width()), m_widthMin(dtypep->widthMin()), m_dtypep(dtypep), m_stage(stage) {}
|
||||
WidthVP(AstNodeDType* dtypep, int width, int widthMin, Stage stage)
|
||||
: m_width(width), m_widthMin(widthMin), m_dtypep(dtypep), m_stage(stage) {}
|
||||
int width() const { return m_width; }
|
||||
int widthMin() const { return m_widthMin?m_widthMin:m_width; }
|
||||
AstNodeDType* dtypep() const { return m_dtypep; }
|
||||
@@ -118,7 +120,6 @@ private:
|
||||
AstFunc* m_funcp; // Current function
|
||||
AstInitial* m_initialp; // Current initial block
|
||||
AstAttrOf* m_attrp; // Current attribute
|
||||
AstNodeDType* m_assDTypep; // Assign LHS data type for assignment pattern
|
||||
bool m_doGenerate; // Do errors later inside generate statement
|
||||
int m_dtTables; // Number of created data type tables
|
||||
|
||||
@@ -386,7 +387,7 @@ private:
|
||||
int width = nodep->elementsConst();
|
||||
if (width > (1<<28)) nodep->v3error("Width of bit range is huge; vector of over 1billion bits: 0x"<<hex<<width);
|
||||
// Note width() not set on range; use elementsConst()
|
||||
if (nodep->littleEndian()) {
|
||||
if (nodep->littleEndian() && !nodep->backp()->castUnpackArrayDType()) {
|
||||
nodep->v3warn(LITENDIAN,"Little bit endian vector: MSB < LSB of bit range: "<<nodep->lsbConst()<<":"<<nodep->msbConst());
|
||||
}
|
||||
}
|
||||
@@ -467,9 +468,9 @@ private:
|
||||
if (m_doGenerate) {
|
||||
UINFO(5, "Selection index out of range inside generate."<<endl);
|
||||
} else {
|
||||
nodep->v3error("Selection index out of range: "
|
||||
<<nodep->msbConst()<<":"<<nodep->lsbConst()
|
||||
<<" outside "<<frommsb<<":"<<fromlsb);
|
||||
nodep->v3warn(SELRANGE,"Selection index out of range: "
|
||||
<<nodep->msbConst()<<":"<<nodep->lsbConst()
|
||||
<<" outside "<<frommsb<<":"<<fromlsb);
|
||||
UINFO(1," Related node: "<<nodep<<endl);
|
||||
}
|
||||
}
|
||||
@@ -804,6 +805,28 @@ private:
|
||||
pushDeletep(nodep); nodep=NULL;
|
||||
//if (debug()) newp->dumpTree(cout," CastOut: ");
|
||||
}
|
||||
virtual void visit(AstCastSize* nodep, AstNUser* vup) {
|
||||
if (!nodep->rhsp()->castConst()) nodep->v3fatalSrc("Unsupported: Non-const cast of size");
|
||||
//if (debug()) nodep->dumpTree(cout," CastPre: ");
|
||||
int width = nodep->rhsp()->castConst()->toSInt();
|
||||
if (width < 1) { nodep->v3error("Size-changing cast to zero or negative size"); width=1; }
|
||||
nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
AstBasicDType* underDtp = nodep->lhsp()->dtypep()->castBasicDType();
|
||||
if (!underDtp) {
|
||||
nodep->v3error("Unsupported: Size-changing cast on non-basic data type");
|
||||
underDtp = nodep->findLogicBoolDType()->castBasicDType();
|
||||
}
|
||||
AstNodeDType* newDtp = (underDtp->keyword().isFourstate()
|
||||
? nodep->findLogicDType(width, width, underDtp->numeric())
|
||||
: nodep->findBitDType(width, width, underDtp->numeric()));
|
||||
nodep->dtypep(newDtp);
|
||||
AstNode* underp = nodep->lhsp()->unlinkFrBack();
|
||||
nodep->replaceWith(underp);
|
||||
if (underp->width()!=width) {
|
||||
fixWidthExtend(underp, newDtp);
|
||||
}
|
||||
pushDeletep(nodep); nodep=NULL;
|
||||
}
|
||||
virtual void visit(AstVar* nodep, AstNUser* vup) {
|
||||
//if (debug()) nodep->dumpTree(cout," InitPre: ");
|
||||
// Must have deterministic constant width
|
||||
@@ -892,6 +915,9 @@ private:
|
||||
if (!nodep->valuep()->castInitArray()) { // No dtype at present, perhaps TODO
|
||||
widthCheck(nodep,"Initial value",nodep->valuep(),nodep->width(),nodep->widthMin());
|
||||
}
|
||||
if (nodep->isDouble() && !nodep->valuep()->isDouble()) {
|
||||
spliceCvtD(nodep->valuep());
|
||||
}
|
||||
}
|
||||
UINFO(4,"varWidthed "<<nodep<<endl);
|
||||
//if (debug()) nodep->dumpTree(cout," InitOut: ");
|
||||
@@ -970,6 +996,7 @@ private:
|
||||
virtual void visit(AstEnumItem* nodep, AstNUser* vup) {
|
||||
UINFO(5," ENUMITEM "<<nodep<<endl);
|
||||
AstNodeDType* vdtypep = vup->c()->dtypep();
|
||||
if (!vdtypep) nodep->v3fatalSrc("ENUMITEM not under ENUM");
|
||||
nodep->dtypep(vdtypep);
|
||||
if (nodep->valuep()) { // else the value will be assigned sequentially
|
||||
int width = vdtypep->width(); // Always from parent type
|
||||
@@ -1049,6 +1076,14 @@ private:
|
||||
nodep->dtypeFrom(nodep->lhsp());
|
||||
}
|
||||
|
||||
virtual void visit(AstIfaceRefDType* nodep, AstNUser* vup) {
|
||||
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||
UINFO(5," IFACEREF "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this, vup);
|
||||
nodep->dtypep(nodep);
|
||||
nodep->widthForce(1, 1); // Not really relevant
|
||||
UINFO(4,"dtWidthed "<<nodep<<endl);
|
||||
}
|
||||
virtual void visit(AstNodeClassDType* nodep, AstNUser* vup) {
|
||||
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||
UINFO(5," NODECLASS "<<nodep<<endl);
|
||||
@@ -1111,7 +1146,7 @@ private:
|
||||
} else {
|
||||
AstSel* newp = new AstSel(nodep->fileline(), nodep->fromp()->unlinkFrBack(),
|
||||
memberp->lsb(), memberp->width());
|
||||
newp->dtypep(memberp);
|
||||
newp->dtypep(memberp->skipRefp()); // Must skip over the member to find the union; as the member may disappear later
|
||||
newp->didWidth(true); // Don't replace dtype with basic type
|
||||
UINFO(9," MEMBERSEL -> "<<newp<<endl);
|
||||
nodep->replaceWith(newp);
|
||||
@@ -1128,12 +1163,17 @@ private:
|
||||
virtual void visit(AstPattern* nodep, AstNUser* vup) {
|
||||
if (nodep->didWidthAndSet()) return;
|
||||
UINFO(9,"PATTERN "<<nodep<<endl);
|
||||
AstNodeDType* oldAssDTypep = m_assDTypep;
|
||||
if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); // data_type '{ pattern }
|
||||
if (!nodep->dtypep() && vup->c()->dtypep()) { // Get it from parent assignment/pin/etc
|
||||
nodep->dtypep(vup->c()->dtypep());
|
||||
}
|
||||
AstNodeDType* vdtypep = nodep->dtypep();
|
||||
if (!vdtypep) nodep->v3error("Unsupported/Illegal: Assignment pattern member not underneath a supported construct: "<<nodep->backp()->prettyTypeName());
|
||||
{
|
||||
if (!m_assDTypep) nodep->v3error("Unsupported/Illegal: Assignment pattern not underneath an assignment");
|
||||
m_assDTypep = m_assDTypep->skipRefp();
|
||||
UINFO(9," adtypep "<<m_assDTypep<<endl);
|
||||
nodep->dtypep(m_assDTypep);
|
||||
vdtypep = vdtypep->skipRefp();
|
||||
nodep->dtypep(vdtypep);
|
||||
UINFO(9," adtypep "<<vdtypep<<endl);
|
||||
nodep->dtypep(vdtypep);
|
||||
for (AstPatMember* patp = nodep->itemsp()->castPatMember(); patp; patp = patp->nextp()->castPatMember()) {
|
||||
// Determine replication count, and replicate initial value as widths need to be individually determined
|
||||
int times = visitPatMemberRep(patp);
|
||||
@@ -1151,7 +1191,10 @@ private:
|
||||
patp->unlinkFrBack();
|
||||
}
|
||||
}
|
||||
if (AstNodeClassDType* classp = m_assDTypep->castNodeClassDType()) {
|
||||
while (AstConstDType* classp = vdtypep->castConstDType()) {
|
||||
vdtypep = classp->subDTypep()->skipRefp();
|
||||
}
|
||||
if (AstNodeClassDType* classp = vdtypep->castNodeClassDType()) {
|
||||
// Due to "default" and tagged patterns, we need to determine
|
||||
// which member each AstPatMember corresponds to before we can
|
||||
// determine the dtypep for that PatMember's value, and then
|
||||
@@ -1205,9 +1248,9 @@ private:
|
||||
patp = it->second;
|
||||
}
|
||||
// Determine initial values
|
||||
m_assDTypep = memp;
|
||||
vdtypep = memp;
|
||||
patp->dtypep(memp);
|
||||
patp->accept(*this,WidthVP(patp->width(),patp->width(),BOTH).p());
|
||||
patp->accept(*this,WidthVP(memp,BOTH).p());
|
||||
// Convert to concat for now
|
||||
if (!newp) newp = patp->lhsp()->unlinkFrBack();
|
||||
else {
|
||||
@@ -1223,16 +1266,16 @@ private:
|
||||
else nodep->v3error("Assignment pattern with no members");
|
||||
pushDeletep(nodep); nodep = NULL; // Deletes defaultp also, if present
|
||||
} else {
|
||||
nodep->v3error("Unsupported: Assignment pattern applies against non struct/union: "<<m_assDTypep->prettyTypeName());
|
||||
nodep->v3error("Unsupported: Assignment pattern applies against non struct/union: "<<vdtypep->prettyTypeName());
|
||||
}
|
||||
}
|
||||
m_assDTypep = oldAssDTypep;
|
||||
}
|
||||
virtual void visit(AstPatMember* nodep, AstNUser* vup) {
|
||||
if (!nodep->dtypep()) nodep->v3fatalSrc("Pattern member type not assigned by AstPattern visitor");
|
||||
if (!m_assDTypep) nodep->v3error("Unsupported/Illegal: Assignment pattern member not underneath an assignment");
|
||||
AstNodeDType* vdtypep = vup->c()->dtypep();
|
||||
if (!vdtypep) nodep->v3fatalSrc("Pattern member type not assigned by AstPattern visitor");
|
||||
nodep->dtypep(vdtypep);
|
||||
nodep->lhsp()->dtypeFrom(nodep);
|
||||
nodep->iterateChildren(*this,WidthVP(nodep->dtypep()->width(),nodep->dtypep()->width(),BOTH).p());
|
||||
nodep->iterateChildren(*this,WidthVP(nodep->dtypep(),BOTH).p());
|
||||
widthCheck(nodep,"LHS",nodep->lhsp(),nodep->width(),nodep->width());
|
||||
}
|
||||
int visitPatMemberRep(AstPatMember* nodep) {
|
||||
@@ -1329,14 +1372,12 @@ private:
|
||||
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
||||
// TOP LEVEL NODE
|
||||
//if (debug()) nodep->dumpTree(cout," AssignPre: ");
|
||||
AstNodeDType* oldAssDTypep = m_assDTypep;
|
||||
{
|
||||
//if (debug()) nodep->dumpTree(cout,"- assin: ");
|
||||
nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
if (!nodep->lhsp()->dtypep()) nodep->v3fatalSrc("How can LHS be untyped?");
|
||||
if (!nodep->lhsp()->dtypep()->widthSized()) nodep->v3fatalSrc("How can LHS be unsized?");
|
||||
m_assDTypep = nodep->lhsp()->dtypep();
|
||||
nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
||||
nodep->rhsp()->iterateAndNext(*this,WidthVP(nodep->lhsp()->dtypep(),ANYSIZE,0,PRELIM).p());
|
||||
//if (debug()) nodep->dumpTree(cout,"- assign: ");
|
||||
if (!nodep->lhsp()->isDouble() && nodep->rhsp()->isDouble()) {
|
||||
spliceCvtS(nodep->rhsp(), false); // Round RHS
|
||||
@@ -1347,7 +1388,7 @@ private:
|
||||
if (awidth==0) {
|
||||
awidth = nodep->rhsp()->width(); // Parameters can propagate by unsized assignment
|
||||
}
|
||||
nodep->rhsp()->iterateAndNext(*this,WidthVP(awidth,awidth,FINAL).p());
|
||||
nodep->rhsp()->iterateAndNext(*this,WidthVP(nodep->lhsp()->dtypep(),awidth,awidth,FINAL).p());
|
||||
nodep->dtypeFrom(nodep->lhsp());
|
||||
nodep->dtypeChgWidth(awidth,awidth); // We know the assign will truncate, so rather
|
||||
// than using "width" and have the optimizer truncate the result, we do
|
||||
@@ -1356,7 +1397,6 @@ private:
|
||||
widthCheck(nodep,"Assign RHS",nodep->rhsp(),awidth,awidth);
|
||||
//if (debug()) nodep->dumpTree(cout," AssignOut: ");
|
||||
}
|
||||
m_assDTypep = oldAssDTypep;
|
||||
}
|
||||
virtual void visit(AstSFormatF* nodep, AstNUser*) {
|
||||
// Excludes NodeDisplay, see below
|
||||
@@ -1646,7 +1686,8 @@ private:
|
||||
V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
|
||||
for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) {
|
||||
AstVar* portp = it->first;
|
||||
AstNode* pinp = it->second;
|
||||
AstArg* argp = it->second;
|
||||
AstNode* pinp = argp->exprp();
|
||||
if (pinp!=NULL) { // Else argument error we'll find later
|
||||
if (accept_mode==0) {
|
||||
// Prelim may cause the node to get replaced; we've lost our
|
||||
@@ -1655,18 +1696,21 @@ private:
|
||||
&& (!pinp->castSFormatF() || pinp->nextp())) { // Not already done
|
||||
UINFO(4," sformat via metacomment: "<<nodep<<endl);
|
||||
AstNRelinker handle;
|
||||
pinp->unlinkFrBackWithNext(&handle); // Format + additional args, if any
|
||||
argp->unlinkFrBackWithNext(&handle); // Format + additional args, if any
|
||||
AstNode* argsp = NULL;
|
||||
if (pinp->nextp()) argsp = pinp->nextp()->unlinkFrBackWithNext();
|
||||
while (AstArg* nextargp = argp->nextp()->castArg()) {
|
||||
argsp = argsp->addNext(nextargp->exprp()->unlinkFrBackWithNext()); // Expression goes to SFormatF
|
||||
nextargp->unlinkFrBack()->deleteTree(); // Remove the call's Arg wrapper
|
||||
}
|
||||
string format;
|
||||
if (pinp->castConst()) format = pinp->castConst()->num().toString();
|
||||
else pinp->v3error("Format to $display-like function must have constant format string");
|
||||
pushDeletep(pinp); pinp=NULL;
|
||||
pushDeletep(argp); argp=NULL;
|
||||
AstSFormatF* newp = new AstSFormatF(nodep->fileline(), format, false, argsp);
|
||||
if (!newp->scopeNamep() && newp->formatScopeTracking()) {
|
||||
newp->scopeNamep(new AstScopeName(newp->fileline()));
|
||||
}
|
||||
handle.relink(newp);
|
||||
handle.relink(new AstArg(newp->fileline(), "", newp));
|
||||
// Connection list is now incorrect (has extra args in it).
|
||||
goto reloop; // so exit early; next loop will correct it
|
||||
}
|
||||
@@ -1681,7 +1725,7 @@ private:
|
||||
handle.relink(newp);
|
||||
pinp = newp;
|
||||
}
|
||||
pinp->accept(*this,WidthVP(portp->width(),portp->widthMin(),PRELIM).p()); pinp=NULL;
|
||||
pinp->accept(*this,WidthVP(portp->dtypep(),PRELIM).p()); pinp=NULL;
|
||||
} else if (accept_mode==1) {
|
||||
// Change data types based on above accept completion
|
||||
if (portp->isDouble()) {
|
||||
@@ -1689,7 +1733,7 @@ private:
|
||||
}
|
||||
} else if (accept_mode==2) {
|
||||
// Do PRELIM again, because above accept may have exited early due to node replacement
|
||||
pinp->accept(*this,WidthVP(portp->width(),portp->widthMin(),BOTH).p());
|
||||
pinp->accept(*this,WidthVP(portp->dtypep(),BOTH).p());
|
||||
if ((portp->isOutput() || portp->isInout())
|
||||
&& pinp->width() != portp->width()) {
|
||||
pinp->v3error("Unsupported: Function output argument '"<<portp->prettyName()<<"'"
|
||||
@@ -2585,7 +2629,6 @@ public:
|
||||
m_funcp = NULL;
|
||||
m_initialp = NULL;
|
||||
m_attrp = NULL;
|
||||
m_assDTypep = NULL;
|
||||
m_doGenerate = doGenerate;
|
||||
m_dtTables = 0;
|
||||
}
|
||||
|
||||
+5
-3
@@ -217,7 +217,7 @@ private:
|
||||
fromp,
|
||||
new AstMul(nodep->fileline(),
|
||||
new AstConst(nodep->fileline(),AstConst::Unsized32(),elwidth),
|
||||
newSubLsbOf(rhsp, fromRange)),
|
||||
subp),
|
||||
new AstConst (nodep->fileline(),AstConst::Unsized32(),elwidth));
|
||||
newp->declRange(fromRange);
|
||||
newp->declElWidth(elwidth);
|
||||
@@ -307,13 +307,15 @@ private:
|
||||
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(), fromRange));
|
||||
new AstRange(nodep->fileline(), newRange));
|
||||
v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
|
||||
newp->dtypeFrom(vardtypep);
|
||||
}
|
||||
if (debug()>=9) newp->dumpTree(cout,"--EXTBTn: ");
|
||||
//if (debug()>=9) newp->dumpTree(cout,"--EXTBTn: ");
|
||||
if (newp->widthMin()!=(int)newp->widthConst()) nodep->v3fatalSrc("Width mismatch");
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL;
|
||||
}
|
||||
else if (ddtypep->castBasicDType()) {
|
||||
|
||||
+2
-3
@@ -228,8 +228,7 @@ void process () {
|
||||
if (!v3Global.opt.xmlOnly()) {
|
||||
// Add top level wrapper with instance pointing to old top
|
||||
// Move packages to under new top
|
||||
// Must do this after we know the width of any parameters
|
||||
// We also do it after coverage/assertion insertion so we don't 'cover' the top level.
|
||||
// Must do this after we know parameters and dtypes (as don't clone dtype decls)
|
||||
V3LinkLevel::wrapTop(v3Global.rootp());
|
||||
}
|
||||
|
||||
@@ -677,7 +676,7 @@ int main(int argc, char** argv, char** env) {
|
||||
}
|
||||
|
||||
// Final steps
|
||||
V3Global::dumpGlobalTree("final.tree",99);
|
||||
V3Global::dumpGlobalTree("final.tree",990);
|
||||
V3Error::abortIfWarnings();
|
||||
|
||||
if (!v3Global.opt.lintOnly() && !v3Global.opt.cdc()
|
||||
|
||||
+36
-26
@@ -32,7 +32,7 @@
|
||||
extern void yyerror(const char*);
|
||||
extern void yyerrorf(const char* format, ...);
|
||||
|
||||
#define STATE_VERILOG_RECENT S09 // State name for most recent Verilog Version
|
||||
#define STATE_VERILOG_RECENT S12 // State name for most recent Verilog Version
|
||||
|
||||
#define PARSEP V3ParseImp::parsep()
|
||||
#define SYMP PARSEP->symp()
|
||||
@@ -135,7 +135,7 @@ void yyerrorf(const char* format, ...) {
|
||||
%a 15000
|
||||
%o 25000
|
||||
|
||||
%s V95 V01 V05 S05 S09
|
||||
%s V95 V01 V05 S05 S09 S12
|
||||
%s STRING ATTRMODE TABLE
|
||||
%s VA5 SA9 PSL VLT
|
||||
%s SYSCHDR SYSCINT SYSCIMP SYSCIMPH SYSCCTOR SYSCDTOR
|
||||
@@ -171,7 +171,7 @@ word [a-zA-Z0-9_]+
|
||||
|
||||
/************************************************************************/
|
||||
/* Verilog 1995 */
|
||||
<V95,V01,V05,VA5,S05,S09,SA9,PSL>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
{ws} { } /* otherwise ignore white-space */
|
||||
{crnl} { NEXTLINE(); } /* Count line numbers */
|
||||
/* Extensions to Verilog set, some specified by PSL */
|
||||
@@ -344,7 +344,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* Verilog 2001 */
|
||||
<V01,V05,VA5,S05,S09,SA9,PSL>{
|
||||
<V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
/* System Tasks */
|
||||
"$signed" { FL; return yD_SIGNED; }
|
||||
"$unsigned" { FL; return yD_UNSIGNED; }
|
||||
@@ -375,13 +375,13 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* Verilog 2005 */
|
||||
<V05,S05,S09,SA9,PSL>{
|
||||
<V05,S05,S09,S12,SA9,PSL>{
|
||||
/* Keywords */
|
||||
"uwire" { FL; return yWIRE; }
|
||||
}
|
||||
|
||||
/* System Verilog 2005 */
|
||||
<S05,S09,PSL>{
|
||||
<S05,S09,S12,PSL>{
|
||||
/* System Tasks */
|
||||
"$bits" { FL; return yD_BITS; }
|
||||
"$clog2" { FL; return yD_CLOG2; }
|
||||
@@ -403,9 +403,9 @@ word [a-zA-Z0-9_]+
|
||||
"$warning" { FL; return yD_WARNING; }
|
||||
/* SV2005 Keywords */
|
||||
"$unit" { FL; return yD_UNIT; } /* Yes, a keyword, not task */
|
||||
"always_comb" { FL; return yALWAYS; }
|
||||
"always_ff" { FL; return yALWAYS; }
|
||||
"always_latch" { FL; return yALWAYS; }
|
||||
"always_comb" { FL; return yALWAYS_COMB; }
|
||||
"always_ff" { FL; return yALWAYS_FF; }
|
||||
"always_latch" { FL; return yALWAYS_LATCH; }
|
||||
"bind" { FL; return yBIND; }
|
||||
"bit" { FL; return yBIT; }
|
||||
"break" { FL; return yBREAK; }
|
||||
@@ -417,6 +417,7 @@ word [a-zA-Z0-9_]+
|
||||
"do" { FL; return yDO; }
|
||||
"endclocking" { FL; return yENDCLOCKING; }
|
||||
"endpackage" { FL; return yENDPACKAGE; }
|
||||
"endinterface" { FL; return yENDINTERFACE; }
|
||||
"endprogram" { FL; return yENDPROGRAM; }
|
||||
"endproperty" { FL; return yENDPROPERTY; }
|
||||
"enum" { FL; return yENUM; }
|
||||
@@ -425,9 +426,11 @@ word [a-zA-Z0-9_]+
|
||||
"iff" { FL; return yIFF; }
|
||||
"import" { FL; return yIMPORT; }
|
||||
"inside" { FL; return yINSIDE; }
|
||||
"interface" { FL; return yINTERFACE; }
|
||||
"int" { FL; return yINT; }
|
||||
"logic" { FL; return yLOGIC; }
|
||||
"longint" { FL; return yLONGINT; }
|
||||
"modport" { FL; return yMODPORT; }
|
||||
"package" { FL; return yPACKAGE; }
|
||||
"packed" { FL; return yPACKED; }
|
||||
"priority" { FL; return yPRIORITY; }
|
||||
@@ -460,7 +463,6 @@ word [a-zA-Z0-9_]+
|
||||
"dist" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"endclass" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"endgroup" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"endinterface" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"endsequence" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"expect" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"extends" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
@@ -470,13 +472,11 @@ word [a-zA-Z0-9_]+
|
||||
"forkjoin" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"ignore_bins" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"illegal_bins" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"interface" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"intersect" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"join_any" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"join_none" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"local" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"matches" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"modport" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"new" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"null" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"protected" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
@@ -498,7 +498,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* SystemVerilog 2005 ONLY not PSL; different rules for PSL as specified below */
|
||||
<S05,S09>{
|
||||
<S05,S09,S12>{
|
||||
/* Keywords */
|
||||
"assert" { FL; return yASSERT; }
|
||||
"const" { FL; return yCONST__LEX; }
|
||||
@@ -513,7 +513,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* SystemVerilog 2009 */
|
||||
<S09,PSL>{
|
||||
<S09,S12,PSL>{
|
||||
/* Keywords */
|
||||
"global" { FL; return yGLOBAL__LEX; }
|
||||
"unique0" { FL; return yUNIQUE0; }
|
||||
@@ -541,8 +541,17 @@ word [a-zA-Z0-9_]+
|
||||
"weak" { yyerrorf("Unsupported: SystemVerilog 2009 reserved word not implemented: %s",yytext); }
|
||||
}
|
||||
|
||||
/* System Verilog 2012 */
|
||||
<S12,PSL>{
|
||||
/* Keywords */
|
||||
"implements" { yyerrorf("Unsupported: SystemVerilog 2012 reserved word not implemented: %s",yytext); }
|
||||
"interconnect" { yyerrorf("Unsupported: SystemVerilog 2012 reserved word not implemented: %s",yytext); }
|
||||
"nettype" { yyerrorf("Unsupported: SystemVerilog 2012 reserved word not implemented: %s",yytext); }
|
||||
"soft" { yyerrorf("Unsupported: SystemVerilog 2012 reserved word not implemented: %s",yytext); }
|
||||
}
|
||||
|
||||
/* Default PLI rule */
|
||||
<V95,V01,V05,VA5,S05,S09,SA9,PSL>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,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
|
||||
@@ -649,7 +658,7 @@ word [a-zA-Z0-9_]+
|
||||
/* PSL */
|
||||
|
||||
/*Entry into PSL; mode change */
|
||||
<V95,V01,V05,VA5,S05,S09,SA9>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9>{
|
||||
"psl" { yy_push_state(PSL); FL; return yPSL; }
|
||||
}
|
||||
|
||||
@@ -738,7 +747,7 @@ word [a-zA-Z0-9_]+
|
||||
/* Meta comments */
|
||||
|
||||
/* Converted from //{cmt}verilator ...{cmt} by preprocessor */
|
||||
<V95,V01,V05,VA5,S05,S09,SA9,PSL>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,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; }
|
||||
@@ -773,11 +782,11 @@ word [a-zA-Z0-9_]+
|
||||
/************************************************************************/
|
||||
|
||||
/* Single character operator thingies */
|
||||
<V95,V01,V05,VA5,S05,S09,SA9>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9>{
|
||||
"{" { FL; return yytext[0]; }
|
||||
"}" { FL; return yytext[0]; }
|
||||
}
|
||||
<V95,V01,V05,VA5,S05,S09,SA9,PSL>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
"!" { FL; return yytext[0]; }
|
||||
"#" { FL; return yytext[0]; }
|
||||
"$" { FL; return yytext[0]; }
|
||||
@@ -809,7 +818,7 @@ word [a-zA-Z0-9_]+
|
||||
/* Operators and multi-character symbols */
|
||||
|
||||
/* Verilog 1995 Operators */
|
||||
<V95,V01,V05,VA5,S05,S09,SA9,PSL>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
"&&" { FL; return yP_ANDAND; }
|
||||
"||" { FL; return yP_OROR; }
|
||||
"<=" { FL; return yP_LTE; }
|
||||
@@ -831,7 +840,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* Verilog 2001 Operators */
|
||||
<V01,V05,VA5,S05,S09,SA9,PSL>{
|
||||
<V01,V05,VA5,S05,S09,S12,SA9,PSL>{
|
||||
"<<<" { FL; return yP_SLEFT; }
|
||||
">>>" { FL; return yP_SSRIGHT; }
|
||||
"**" { FL; return yP_POW; }
|
||||
@@ -841,7 +850,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* SystemVerilog Operators */
|
||||
<S05,S09>{
|
||||
<S05,S09,S12>{
|
||||
"'" { FL; return yP_TICK; }
|
||||
"'{" { FL; return yP_TICKBRA; }
|
||||
"==?" { FL; return yP_WILDEQUAL; }
|
||||
@@ -890,7 +899,7 @@ word [a-zA-Z0-9_]+
|
||||
}
|
||||
|
||||
/* Identifiers and numbers */
|
||||
<V95,V01,V05,VA5,S05,S09,SA9,PSL,VLT>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL,VLT>{
|
||||
{escid} { FL; yylval.strp = PARSEP->newString
|
||||
(AstNode::encodeName(string(yytext+1))); // +1 to skip the backslash
|
||||
return yaID__LEX;
|
||||
@@ -966,7 +975,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,SA9>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9>{
|
||||
"(*"({ws}|{crnl})*({id}|{escid}) { yymore(); yy_push_state(ATTRMODE); } // Doesn't match (*), but (* attr_spec
|
||||
}
|
||||
|
||||
@@ -983,7 +992,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,SA9,PSL,VLT,SYSCHDR,SYSCINT,SYSCIMP,SYSCIMPH,SYSCCTOR,SYSCDTOR,IGNORE>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,PSL,VLT,SYSCHDR,SYSCINT,SYSCIMP,SYSCIMPH,SYSCCTOR,SYSCDTOR,IGNORE>{
|
||||
"`accelerate" { } // Verilog-XL compatibility
|
||||
"`autoexpand_vectornets" { } // Verilog-XL compatibility
|
||||
"`celldefine" { PARSEP->inCellDefine(true); }
|
||||
@@ -1027,6 +1036,7 @@ word [a-zA-Z0-9_]+
|
||||
"`begin_keywords"[ \t]*\"VAMS[-0-9.]*\" { yy_push_state(VA5); PARSEP->pushBeginKeywords(YY_START); }
|
||||
"`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); }
|
||||
"`end_keywords" { yy_pop_state(); if (!PARSEP->popBeginKeywords()) yyerrorf("`end_keywords when not inside `begin_keywords block"); }
|
||||
|
||||
@@ -1058,7 +1068,7 @@ word [a-zA-Z0-9_]+
|
||||
/************************************************************************/
|
||||
/* Default rules - leave last */
|
||||
|
||||
<V95,V01,V05,VA5,S05,S09,SA9,PSL,VLT>{
|
||||
<V95,V01,V05,VA5,S05,S09,S12,SA9,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. */
|
||||
|
||||
+162
-23
@@ -85,6 +85,7 @@ public:
|
||||
}
|
||||
|
||||
// METHODS
|
||||
void argWrapList(AstNodeFTaskRef* nodep);
|
||||
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);
|
||||
@@ -273,6 +274,9 @@ class AstSenTree;
|
||||
// Double underscores "yX__Y" means token X followed by Y,
|
||||
// and "yX__ETC" means X folled by everything but Y(s).
|
||||
%token<fl> yALWAYS "always"
|
||||
%token<fl> yALWAYS_FF "always_ff"
|
||||
%token<fl> yALWAYS_COMB "always_comb"
|
||||
%token<fl> yALWAYS_LATCH "always_latch"
|
||||
%token<fl> yAND "and"
|
||||
%token<fl> yASSERT "assert"
|
||||
%token<fl> yASSIGN "assign"
|
||||
@@ -307,6 +311,7 @@ class AstSenTree;
|
||||
%token<fl> yENDCLOCKING "endclocking"
|
||||
%token<fl> yENDFUNCTION "endfunction"
|
||||
%token<fl> yENDGENERATE "endgenerate"
|
||||
%token<fl> yENDINTERFACE "endinterface"
|
||||
%token<fl> yENDMODULE "endmodule"
|
||||
%token<fl> yENDPACKAGE "endpackage"
|
||||
%token<fl> yENDPRIMITIVE "endprimitive"
|
||||
@@ -334,9 +339,11 @@ class AstSenTree;
|
||||
%token<fl> yINSIDE "inside"
|
||||
%token<fl> yINT "int"
|
||||
%token<fl> yINTEGER "integer"
|
||||
%token<fl> yINTERFACE "interface"
|
||||
%token<fl> yLOCALPARAM "localparam"
|
||||
%token<fl> yLOGIC "logic"
|
||||
%token<fl> yLONGINT "longint"
|
||||
%token<fl> yMODPORT "modport"
|
||||
%token<fl> yMODULE "module"
|
||||
%token<fl> yNAND "nand"
|
||||
%token<fl> yNEGEDGE "negedge"
|
||||
@@ -647,11 +654,11 @@ descriptionList: // IEEE: part of source_text
|
||||
description: // ==IEEE: description
|
||||
module_declaration { }
|
||||
// // udp_declaration moved into module_declaration
|
||||
//UNSUP interface_declaration { }
|
||||
| interface_declaration { }
|
||||
| program_declaration { }
|
||||
| package_declaration { }
|
||||
| package_item { if ($1) GRAMMARP->unitPackage($1->fileline())->addStmtp($1); }
|
||||
//UNSUP bind_directive { }
|
||||
| bind_directive { if ($1) GRAMMARP->unitPackage($1->fileline())->addStmtp($1); }
|
||||
// unsupported // IEEE: config_declaration
|
||||
// // Verilator only
|
||||
| vltItem { }
|
||||
@@ -841,14 +848,18 @@ port<nodep>: // ==IEEE: port
|
||||
// // IEEE: interface_port_header port_identifier { unpacked_dimension }
|
||||
// // Expanded interface_port_header
|
||||
// // We use instantCb here because the non-port form looks just like a module instantiation
|
||||
//UNSUP portDirNetE id/*interface*/ idAny/*port*/ rangeListE sigAttrListE
|
||||
//UNSUP { VARDTYPE($2); VARDONEA($<fl>3, $3, $4); PARSEP->instantCb($<fl>2, $2, $3, $4); PINNUMINC(); }
|
||||
//UNSUP portDirNetE yINTERFACE idAny/*port*/ rangeListE sigAttrListE
|
||||
//UNSUP { VARDTYPE($2); VARDONEA($<fl>3, $3, $4); PINNUMINC(); }
|
||||
//UNSUP portDirNetE id/*interface*/ '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE
|
||||
//UNSUP { VARDTYPE($2); VARDONEA($<fl>5, $5, $6); PARSEP->instantCb($<fl>2, $2, $5, $6); PINNUMINC(); }
|
||||
//UNSUP portDirNetE yINTERFACE '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE
|
||||
//UNSUP { VARDTYPE($2); VARDONEA($<fl>5, $5, $6); PINNUMINC(); }
|
||||
portDirNetE id/*interface*/ portSig variable_dimensionListE sigAttrListE
|
||||
{ $$ = $3; VARDECL(AstVarType::IFACEREF); VARIO(UNKNOWN);
|
||||
VARDTYPE(new AstIfaceRefDType($<fl>2,"",*$2));
|
||||
$$->addNextNull(VARDONEP($$,$4,$5)); }
|
||||
| portDirNetE id/*interface*/ '.' idAny/*modport*/ portSig rangeListE sigAttrListE
|
||||
{ $$ = $5; VARDECL(AstVarType::IFACEREF); VARIO(UNKNOWN);
|
||||
VARDTYPE(new AstIfaceRefDType($<fl>2,"",*$2,*$4));
|
||||
$$->addNextNull(VARDONEP($$,$6,$7)); }
|
||||
| portDirNetE yINTERFACE portSig rangeListE sigAttrListE
|
||||
{ $<fl>2->v3error("Unsupported: virtual interfaces"); $$=NULL; }
|
||||
| portDirNetE yINTERFACE '.' idAny/*modport*/ portSig rangeListE sigAttrListE
|
||||
{ $<fl>2->v3error("Unsupported: virtual interfaces"); $$=NULL; }
|
||||
//
|
||||
// // IEEE: ansi_port_declaration, with [port_direction] removed
|
||||
// // IEEE: [ net_port_header | interface_port_header ] port_identifier { unpacked_dimension } [ '=' constant_expression ]
|
||||
@@ -886,7 +897,7 @@ port<nodep>: // ==IEEE: port
|
||||
//UNSUP portDirNetE /*implicit*/ '.' portSig '(' portAssignExprE ')' sigAttrListE
|
||||
//UNSUP { UNSUP }
|
||||
//
|
||||
portDirNetE data_type portSig variable_dimensionListE sigAttrListE
|
||||
| portDirNetE data_type portSig variable_dimensionListE sigAttrListE
|
||||
{ $$=$3; VARDTYPE($2); $$->addNextNull(VARDONEP($$,$4,$5)); }
|
||||
| portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE
|
||||
{ $$=$4; VARDTYPE($3); $$->addNextNull(VARDONEP($$,$5,$6)); }
|
||||
@@ -929,6 +940,54 @@ portSig<nodep>:
|
||||
//**********************************************************************
|
||||
// Interface headers
|
||||
|
||||
interface_declaration: // IEEE: interface_declaration + interface_nonansi_header + interface_ansi_header:
|
||||
// // timeunits_delcarationE is instead in interface_item
|
||||
intFront parameter_port_listE portsStarE ';'
|
||||
interface_itemListE yENDINTERFACE endLabelE
|
||||
{ if ($2) $1->addStmtp($2);
|
||||
if ($3) $1->addStmtp($3);
|
||||
if ($5) $1->addStmtp($5);
|
||||
SYMP->popScope($1); }
|
||||
//UNSUP yEXTERN intFront parameter_port_listE portsStarE ';' { }
|
||||
;
|
||||
|
||||
intFront<modulep>:
|
||||
yINTERFACE lifetimeE idAny/*new_interface*/
|
||||
{ $$ = new AstIface($1,*$3);
|
||||
$$->inLibrary(true);
|
||||
PARSEP->rootp()->addModulep($$);
|
||||
SYMP->pushNew($$); }
|
||||
;
|
||||
|
||||
interface_itemListE<nodep>:
|
||||
/* empty */ { $$ = NULL; }
|
||||
| interface_itemList { $$ = $1; }
|
||||
;
|
||||
|
||||
interface_itemList<nodep>:
|
||||
interface_item { $$ = $1; }
|
||||
| interface_itemList interface_item { $$ = $1->addNextNull($2); }
|
||||
;
|
||||
|
||||
interface_item<nodep>: // IEEE: interface_item + non_port_interface_item
|
||||
port_declaration ';' { $$ = $1; }
|
||||
// // IEEE: non_port_interface_item
|
||||
//UNSUP generate_region { $$ = $1; }
|
||||
| interface_or_generate_item { $$ = $1; }
|
||||
//UNSUP program_declaration { $$ = $1; }
|
||||
//UNSUP interface_declaration { $$ = $1; }
|
||||
| timeunits_declaration { $$ = $1; }
|
||||
// // See note in interface_or_generate item
|
||||
| module_common_item { $$ = $1; }
|
||||
;
|
||||
|
||||
interface_or_generate_item<nodep>: // ==IEEE: interface_or_generate_item
|
||||
// // module_common_item in interface_item, as otherwise duplicated
|
||||
// // with module_or_generate_item's module_common_item
|
||||
modport_declaration { $$ = $1; }
|
||||
//UNSUP extern_tf_declaration { $$ = $1; }
|
||||
;
|
||||
|
||||
//**********************************************************************
|
||||
// Program headers
|
||||
|
||||
@@ -986,6 +1045,46 @@ program_generate_item<nodep>: // ==IEEE: program_generate_item
|
||||
//UNSUP elaboration_system_task { $$ = $1; }
|
||||
;
|
||||
|
||||
modport_declaration<nodep>: // ==IEEE: modport_declaration
|
||||
yMODPORT modport_itemList ';' { $$ = $2; }
|
||||
;
|
||||
|
||||
modport_itemList<nodep>: // IEEE: part of modport_declaration
|
||||
modport_item { $$ = $1; }
|
||||
| modport_itemList ',' modport_item { $$ = $1->addNextNull($3); }
|
||||
;
|
||||
|
||||
modport_item<nodep>: // ==IEEE: modport_item
|
||||
id/*new-modport*/ '(' modportPortsDeclList ')' { $$ = new AstModport($2,*$1,$3); }
|
||||
;
|
||||
|
||||
modportPortsDeclList<modportvarrefp>:
|
||||
modportPortsDecl { $$ = $1; }
|
||||
| modportPortsDeclList ',' modportPortsDecl { $$ = $1->addNextNull($3)->castModportVarRef(); }
|
||||
;
|
||||
|
||||
// IEEE: modport_ports_declaration + modport_simple_ports_declaration
|
||||
// + (modport_tf_ports_declaration+import_export) + modport_clocking_declaration
|
||||
// We've expanded the lists each take to instead just have standalone ID ports.
|
||||
// We track the type as with the V2k series of defines, then create as each ID is seen.
|
||||
modportPortsDecl<modportvarrefp>:
|
||||
// // IEEE: modport_simple_ports_declaration
|
||||
port_direction modportSimplePort { $$ = new AstModportVarRef($<fl>1,*$2,GRAMMARP->m_varIO); }
|
||||
// // IEEE: modport_clocking_declaration
|
||||
//UNSUP yCLOCKING idAny/*clocking_identifier*/ { }
|
||||
//UNSUP yIMPORT modport_tf_port { }
|
||||
//UNSUP yEXPORT modport_tf_port { }
|
||||
// Continuations of above after a comma.
|
||||
// // IEEE: modport_simple_ports_declaration
|
||||
| modportSimplePort { $$ = new AstModportVarRef($<fl>1,*$1,AstVarType::INOUT); }
|
||||
;
|
||||
|
||||
modportSimplePort<strp>: // IEEE: modport_simple_port or modport_tf_port, depending what keyword was earlier
|
||||
id { $$ = $1; }
|
||||
//UNSUP '.' idAny '(' ')' { }
|
||||
//UNSUP '.' idAny '(' expr ')' { }
|
||||
;
|
||||
|
||||
//************************************************
|
||||
// Variable Declarations
|
||||
|
||||
@@ -1549,7 +1648,10 @@ module_common_item<nodep>: // ==IEEE: module_common_item
|
||||
| final_construct { $$ = $1; }
|
||||
// // IEEE: always_construct
|
||||
// // Verilator only - event_control attached to always
|
||||
| yALWAYS event_controlE stmtBlock { $$ = new AstAlways($1,$2,$3); }
|
||||
| yALWAYS event_controlE stmtBlock { $$ = new AstAlways($1,VAlwaysKwd::ALWAYS, $2,$3); }
|
||||
| yALWAYS_FF event_controlE stmtBlock { $$ = new AstAlways($1,VAlwaysKwd::ALWAYS_FF, $2,$3); }
|
||||
| yALWAYS_COMB event_controlE stmtBlock { $$ = new AstAlways($1,VAlwaysKwd::ALWAYS_COMB, $2,$3); }
|
||||
| yALWAYS_LATCH event_controlE stmtBlock { $$ = new AstAlways($1,VAlwaysKwd::ALWAYS_LATCH, $2,$3); }
|
||||
| loop_generate_construct { $$ = $1; }
|
||||
| conditional_generate_construct { $$ = $1; }
|
||||
// // Verilator only
|
||||
@@ -2184,6 +2286,7 @@ statementVerilatorPragmas<nodep>:
|
||||
|
||||
foperator_assignment<nodep>: // IEEE: operator_assignment (for first part of expression)
|
||||
fexprLvalue '=' delayE expr { $$ = new AstAssign($2,$1,$4); }
|
||||
| fexprLvalue '=' yD_FOPEN '(' expr ')' { $$ = NULL; $3->v3error("Unsupported: $fopen with multichannel descriptor. Add ,\"w\" as second argument to open a file descriptor."); }
|
||||
| fexprLvalue '=' yD_FOPEN '(' expr ',' expr ')' { $$ = new AstFOpen($3,$1,$5,$7); }
|
||||
//
|
||||
//UNSUP ~f~exprLvalue '=' delay_or_event_controlE expr { UNSUP }
|
||||
@@ -2203,10 +2306,10 @@ foperator_assignment<nodep>: // IEEE: operator_assignment (for first part of exp
|
||||
|
||||
finc_or_dec_expression<nodep>: // ==IEEE: inc_or_dec_expression
|
||||
//UNSUP: Generic scopes in incrementes
|
||||
varRefBase yP_PLUSPLUS { $$ = new AstAssign($2,$1,new AstAdd ($2,$1->cloneTree(true),new AstConst($2,V3Number($2,"'b1")))); }
|
||||
| varRefBase yP_MINUSMINUS { $$ = new AstAssign($2,$1,new AstSub ($2,$1->cloneTree(true),new AstConst($2,V3Number($2,"'b1")))); }
|
||||
| yP_PLUSPLUS varRefBase { $$ = new AstAssign($1,$2,new AstAdd ($1,$2->cloneTree(true),new AstConst($1,V3Number($1,"'b1")))); }
|
||||
| yP_MINUSMINUS varRefBase { $$ = new AstAssign($1,$2,new AstSub ($1,$2->cloneTree(true),new AstConst($1,V3Number($1,"'b1")))); }
|
||||
fexprLvalue yP_PLUSPLUS { $$ = new AstAssign($2,$1,new AstAdd ($2,$1->cloneTree(true),new AstConst($2,V3Number($2,"'b1")))); }
|
||||
| fexprLvalue yP_MINUSMINUS { $$ = new AstAssign($2,$1,new AstSub ($2,$1->cloneTree(true),new AstConst($2,V3Number($2,"'b1")))); }
|
||||
| yP_PLUSPLUS fexprLvalue { $$ = new AstAssign($1,$2,new AstAdd ($1,$2->cloneTree(true),new AstConst($1,V3Number($1,"'b1")))); }
|
||||
| yP_MINUSMINUS fexprLvalue { $$ = new AstAssign($1,$2,new AstSub ($1,$2->cloneTree(true),new AstConst($1,V3Number($1,"'b1")))); }
|
||||
;
|
||||
|
||||
//************************************************
|
||||
@@ -2313,7 +2416,7 @@ patternKey<nodep>: // IEEE: merge structure_pattern_key, array_pattern_key, ass
|
||||
| yaID__ETC { $$ = new AstText($<fl>1,*$1); }
|
||||
;
|
||||
|
||||
assignment_pattern<nodep>: // ==IEEE: assignment_pattern
|
||||
assignment_pattern<patternp>: // ==IEEE: assignment_pattern
|
||||
// This doesn't match the text of the spec. I think a : is missing, or example code needed
|
||||
// yP_TICKBRA constExpr exprList '}' { $$="'{"+$2+" "+$3"}"; }
|
||||
// // "'{ const_expression }" is same as patternList with one entry
|
||||
@@ -2368,6 +2471,7 @@ funcRef<nodep>: // IEEE: part of tf_call
|
||||
// // property_instance property_identifier property_actual_arg
|
||||
// // sequence_instance sequence_identifier sequence_actual_arg
|
||||
// // let_expression let_identifier let_actual_arg
|
||||
//
|
||||
id '(' list_of_argumentsE ')' { $$ = new AstFuncRef($2, *$1, $3); }
|
||||
| package_scopeIdFollows id '(' list_of_argumentsE ')' { $$ = AstDot::newIfPkg($<fl>2, $1, new AstFuncRef($<fl>2,*$2,$4)); }
|
||||
//UNSUP: idDotted is really just id to allow dotted method calls
|
||||
@@ -2395,7 +2499,7 @@ system_t_call<nodep>: // IEEE: system_tf_call (as task)
|
||||
| yaD_IGNORE '(' exprList ')' { $$ = new AstSysIgnore($<fl>1,$3); }
|
||||
//
|
||||
| yaD_DPI parenE { $$ = new AstTaskRef($<fl>1,*$1,NULL); }
|
||||
| yaD_DPI '(' exprList ')' { $$ = new AstTaskRef($2,*$1,$3); }
|
||||
| yaD_DPI '(' exprList ')' { $$ = new AstTaskRef($2,*$1,$3); GRAMMARP->argWrapList($$->castTaskRef()); }
|
||||
//
|
||||
| yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? NULL : new AstUCStmt($1,$3)); }
|
||||
| yD_FCLOSE '(' idClassSel ')' { $$ = new AstFClose($1, $3); }
|
||||
@@ -2440,7 +2544,7 @@ system_f_call<nodep>: // IEEE: system_tf_call (as func)
|
||||
| yaD_IGNORE '(' exprList ')' { $$ = new AstConst($2,V3Number($2,"'b0")); } // Unsized 0
|
||||
//
|
||||
| yaD_DPI parenE { $$ = new AstFuncRef($<fl>1,*$1,NULL); }
|
||||
| yaD_DPI '(' exprList ')' { $$ = new AstFuncRef($2,*$1,$3); }
|
||||
| yaD_DPI '(' exprList ')' { $$ = new AstFuncRef($2,*$1,$3); GRAMMARP->argWrapList($$->castFuncRef()); }
|
||||
//
|
||||
| yD_BITS '(' data_type ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_BITS,$3); }
|
||||
| yD_BITS '(' data_type ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_BITS,$3,$5); }
|
||||
@@ -2496,9 +2600,10 @@ system_f_call<nodep>: // IEEE: system_tf_call (as func)
|
||||
;
|
||||
|
||||
list_of_argumentsE<nodep>: // IEEE: [list_of_arguments]
|
||||
/* empty */ { $$ = NULL; }
|
||||
| argsExprList { $$ = $1; }
|
||||
//UNSUP empty arguments with just ,,
|
||||
argsDottedList { $$ = $1; }
|
||||
| argsExprListE { if ($1->castArg() && $1->castArg()->emptyConnectNoNext()) { $1->deleteTree(); $$ = NULL; } // Mis-created when have 'func()'
|
||||
/*cont*/ else $$ = $1; }
|
||||
| argsExprListE ',' argsDottedList { $$ = $1->addNextNull($3); }
|
||||
;
|
||||
|
||||
task_declaration<ftaskp>: // ==IEEE: task_declaration
|
||||
@@ -2824,6 +2929,7 @@ expr<nodep>: // IEEE: part of expression/constant_expression/primary
|
||||
// // Spec only allows primary with addition of a type reference
|
||||
// // We'll be more general, and later assert LHS was a type.
|
||||
//UNSUP ~l~expr yP_TICK '(' expr ')' { UNSUP }
|
||||
| yaINTNUM yP_TICK '(' expr ')' { $$ = new AstCastSize($2,$4,new AstConst($1->fileline(),*$1)); }
|
||||
//
|
||||
// // IEEE: assignment_pattern_expression
|
||||
// // IEEE: streaming_concatenation
|
||||
@@ -2875,7 +2981,7 @@ exprOkLvalue<nodep>: // expression that's also OK to use as a variable_lvalue
|
||||
// // IEEE: [ assignment_pattern_expression_type ] == [ ps_type_id /ps_paremeter_id/data_type]
|
||||
// // We allow more here than the spec requires
|
||||
//UNSUP ~l~exprScope assignment_pattern { UNSUP }
|
||||
//UNSUP data_type assignment_pattern { UNSUP }
|
||||
| data_type assignment_pattern { $$ = $2; $2->childDTypep($1); }
|
||||
| assignment_pattern { $$ = $1; }
|
||||
//
|
||||
//UNSUP streaming_concatenation { UNSUP }
|
||||
@@ -2960,6 +3066,26 @@ argsExprList<nodep>: // IEEE: part of list_of_arguments (used where ,, isn't le
|
||||
| argsExprList ',' expr { $$ = $1->addNext($3); }
|
||||
;
|
||||
|
||||
argsExprListE<nodep>: // IEEE: part of list_of_arguments
|
||||
argsExprOneE { $$ = $1; }
|
||||
| argsExprListE ',' argsExprOneE { $$ = $1->addNext($3); }
|
||||
;
|
||||
|
||||
argsExprOneE<nodep>: // IEEE: part of list_of_arguments
|
||||
/*empty*/ { $$ = new AstArg(CRELINE(),"",NULL); }
|
||||
| expr { $$ = new AstArg(CRELINE(),"",$1); }
|
||||
;
|
||||
|
||||
argsDottedList<nodep>: // IEEE: part of list_of_arguments
|
||||
argsDotted { $$ = $1; }
|
||||
| argsDottedList ',' argsDotted { $$ = $1->addNextNull($3); }
|
||||
;
|
||||
|
||||
argsDotted<nodep>: // IEEE: part of list_of_arguments
|
||||
'.' idAny '(' ')' { $$ = new AstArg($1,*$2,NULL); }
|
||||
| '.' idAny '(' expr ')' { $$ = new AstArg($1,*$2,$4); }
|
||||
;
|
||||
|
||||
stream_expression<nodep>: // ==IEEE: stream_expression
|
||||
// // IEEE: array_range_expression expanded below
|
||||
expr { $$ = $1; }
|
||||
@@ -3487,6 +3613,16 @@ void V3ParseImp::parserClear() {
|
||||
VARDTYPE(NULL);
|
||||
}
|
||||
|
||||
void V3ParseGrammar::argWrapList(AstNodeFTaskRef* nodep) {
|
||||
// Convert list of expressions to list of arguments
|
||||
AstNode* outp = NULL;
|
||||
while (nodep->pinsp()) {
|
||||
AstNode* exprp = nodep->pinsp()->unlinkFrBack();
|
||||
outp = outp->addNext(new AstArg(exprp->fileline(), "", exprp));
|
||||
}
|
||||
if (outp) nodep->addPinsp(outp);
|
||||
}
|
||||
|
||||
AstNode* V3ParseGrammar::createSupplyExpr(FileLine* fileline, string name, int value) {
|
||||
FileLine* newfl = new FileLine (fileline);
|
||||
newfl->warnOff(V3ErrorCode::WIDTH, true);
|
||||
@@ -3527,6 +3663,9 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, string name, AstRange
|
||||
return NULL;
|
||||
}
|
||||
AstVarType type = GRAMMARP->m_varIO;
|
||||
if (dtypep->castIfaceRefDType()) {
|
||||
if (arrayp) { fileline->v3error("Unsupported: Arrayed interfaces"); arrayp=NULL; }
|
||||
}
|
||||
if (!dtypep) { // Created implicitly
|
||||
dtypep = new AstBasicDType(fileline, LOGIC_IMPLICIT);
|
||||
} else { // May make new variables with same type, so clone
|
||||
|
||||
+77
-28
@@ -317,8 +317,10 @@ sub new {
|
||||
pl_filename => undef, # Name of .pl file to get setup from
|
||||
make_top_shell => 1, # Make a default __top.v file
|
||||
make_main => 1, # Make __main.cpp
|
||||
make_pli => 0, # need to compile pli
|
||||
sim_time => 1100,
|
||||
benchmark => $opt_benchmark,
|
||||
verbose => $opt_verbose,
|
||||
run_env => '',
|
||||
# All compilers
|
||||
v_flags => [split(/\s+/,(" -f input.vc "
|
||||
@@ -331,7 +333,8 @@ sub new {
|
||||
v_flags2 => [], # Overridden in some sim files
|
||||
v_other_filenames => [], # After the filename so we can spec multiple files
|
||||
all_run_flags => [],
|
||||
# ATSIM
|
||||
pli_flags => ["-I$ENV{VERILATOR_ROOT}/include/vltstd -fPIC -export-dynamic -shared -o $self->{obj_dir}/libvpi.so"],
|
||||
# ATSIM
|
||||
atsim => 0,
|
||||
atsim_flags => [split(/\s+/,"-c +sv +define+ATSIM"),
|
||||
"+sv_dir+$self->{obj_dir}/.athdl_compile"],
|
||||
@@ -348,6 +351,7 @@ sub new {
|
||||
iv => 0,
|
||||
iv_flags => [split(/\s+/,"+define+iverilog -o $self->{obj_dir}/simiv")],
|
||||
iv_flags2 => [], # Overridden in some sim files
|
||||
iv_pli => 0, # need to use pli
|
||||
iv_run_flags => [],
|
||||
# VCS
|
||||
vcs => 0,
|
||||
@@ -365,6 +369,7 @@ sub new {
|
||||
'v3' => 0,
|
||||
verilator_flags => ["-cc",
|
||||
"-Mdir $self->{obj_dir}",
|
||||
"-OD", # As currently disabled unless -O3
|
||||
"--debug-check"],
|
||||
verilator_flags2 => [],
|
||||
verilator_make_gcc => 1,
|
||||
@@ -657,6 +662,16 @@ sub compile {
|
||||
else {
|
||||
$self->error("No compile step for this simulator");
|
||||
}
|
||||
|
||||
if ($param{make_pli}) {
|
||||
$self->oprint("Compile vpi\n");
|
||||
my @cmd = ('g++', @{$param{pli_flags}}, "-DIS_VPI", "$self->{t_dir}/$self->{name}.cpp");
|
||||
|
||||
$self->_run(logfile=>"$self->{obj_dir}/pli_compile.log",
|
||||
fails=>$param{fails},
|
||||
cmd=>\@cmd);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -693,12 +708,16 @@ sub execute {
|
||||
);
|
||||
}
|
||||
elsif ($param{iv}) {
|
||||
my @cmd = ($run_env."$self->{obj_dir}/simiv",
|
||||
@{$param{iv_run_flags}},
|
||||
@{$param{all_run_flags}},
|
||||
);
|
||||
if ($param{iv_pli}) {
|
||||
unshift @cmd, "vvp -m $self->{obj_dir}/libvpi.so";
|
||||
}
|
||||
$self->_run(logfile=>"$self->{obj_dir}/iv_sim.log",
|
||||
fails=>$param{fails},
|
||||
cmd=>[$run_env."$self->{obj_dir}/simiv",
|
||||
@{$param{iv_run_flags}},
|
||||
@{$param{all_run_flags}},
|
||||
],
|
||||
cmd=> \@cmd,
|
||||
%param,
|
||||
expect=>$param{iv_run_expect}, # non-verilator expect isn't the same
|
||||
);
|
||||
@@ -860,30 +879,50 @@ sub _run {
|
||||
print " > $param{logfile}" if $param{logfile};
|
||||
print "\n";
|
||||
|
||||
if ($param{logfile}) {
|
||||
open(SAVEOUT, ">&STDOUT") or die "%Error: Can't dup stdout";
|
||||
open(SAVEERR, ">&STDERR") or die "%Error: Can't dup stderr";
|
||||
if (0) {close(SAVEOUT); close(SAVEERR);} # Prevent unused warning
|
||||
if ($param{tee}) {
|
||||
open(STDOUT, "|tee $param{logfile}") or die "%Error: Can't redirect stdout";
|
||||
} else {
|
||||
open(STDOUT, ">$param{logfile}") or die "%Error: Can't open $param{logfile}";
|
||||
# Execute command redirecting output, keeping order between stderr and stdout.
|
||||
# Must do low-level IO so GCC interaction works (can't be line-based)
|
||||
my $status;
|
||||
{
|
||||
pipe(PARENTRD, CHILDWR) or die "%Error: Can't Pipe, stopped";
|
||||
autoflush PARENTRD 1;
|
||||
autoflush CHILDWR 1;
|
||||
my $logfh;
|
||||
if ($param{logfile}) {
|
||||
$logfh = IO::File->new(">$param{logfile}") or die "%Error: Can't open $param{logfile}";
|
||||
}
|
||||
open(STDERR, ">&STDOUT") or die "%Error: Can't dup stdout";
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
my $pid=fork();
|
||||
if ($pid) { # Parent
|
||||
close CHILDWR;
|
||||
while (1) {
|
||||
my $buf = '';
|
||||
my $got = sysread PARENTRD,$buf,10000;
|
||||
last if defined $got && $got==0;
|
||||
print $buf if $param{tee};
|
||||
print $logfh $buf if $logfh;
|
||||
}
|
||||
close PARENTRD;
|
||||
close $logfh if $logfh;
|
||||
}
|
||||
else { # Child
|
||||
close PARENTRD;
|
||||
close $logfh if $logfh;
|
||||
# Reset signals
|
||||
$SIG{ALRM} = 'DEFAULT';
|
||||
$SIG{CHLD} = 'DEFAULT';
|
||||
# Logging
|
||||
open(STDOUT, ">&CHILDWR") or croak "%Error: Can't redirect stdout, stopped";
|
||||
open(STDERR, ">&STDOUT") or croak "%Error: Can't dup stdout, stopped";
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
system "$command";
|
||||
exit ($? ? 10 : 0); # $?<<8 misses coredumps
|
||||
}
|
||||
waitpid($pid,0);
|
||||
$status = $? || 0;
|
||||
}
|
||||
|
||||
system "$command";
|
||||
my $status = $?;
|
||||
flush STDOUT;
|
||||
flush STDERR;
|
||||
|
||||
if ($param{logfile}) {
|
||||
open (STDOUT, ">&SAVEOUT");
|
||||
open (STDERR, ">&SAVEERR");
|
||||
}
|
||||
|
||||
if (!$param{fails} && $status) {
|
||||
$self->error("Exec of $param{cmd}[0] failed\n");
|
||||
}
|
||||
@@ -1276,14 +1315,23 @@ sub _read_inputs_v {
|
||||
my $filename = $self->top_filename;
|
||||
$filename = "$self->{t_dir}/$filename" if !-r $filename;
|
||||
my $fh = IO::File->new("<$filename") or die "%Error: $! $filename,";
|
||||
my $get_sigs=1;
|
||||
my %inputs;
|
||||
while (defined(my $line = $fh->getline)) {
|
||||
if ($line =~ /^\s*input\s*(\S+)\s*(\/[^\/]+\/|)\s*;/) {
|
||||
$self->{inputs}{$1} = $1;
|
||||
if ($get_sigs) {
|
||||
if ($line =~ /^\s*input\s*(\S+)\s*(\/[^\/]+\/|)\s*;/) {
|
||||
$inputs{$1} = $1;
|
||||
}
|
||||
if ($line =~ /^\s*(function|task|endmodule)/) {
|
||||
$get_sigs = 0;
|
||||
}
|
||||
}
|
||||
if ($line =~ /^\s*(function|task|endmodule)/) {
|
||||
last;
|
||||
if ($line =~ /^\s*module\s+t\b/) { # Ignore any earlier inputs; Module 't' has precedence
|
||||
%inputs = ();
|
||||
$get_sigs = 1;
|
||||
}
|
||||
}
|
||||
$self->{inputs}{$_} = $inputs{$_} foreach keys %inputs;
|
||||
$fh->close();
|
||||
}
|
||||
|
||||
@@ -1503,6 +1551,7 @@ sub sig_child {}
|
||||
sub kill_tree_all {}
|
||||
sub wait_all {}
|
||||
sub ready {}
|
||||
sub running {}
|
||||
|
||||
#######################################################################
|
||||
1;
|
||||
|
||||
@@ -14,9 +14,9 @@ use Cwd qw(chdir);
|
||||
|
||||
my @args = @ARGV;
|
||||
chdir("$FindBin::Bin/..");
|
||||
$ENV{PWD} = Cwd::getcwd(); # Else chdir leaves the .. which confuses later commands
|
||||
|
||||
@args = map { s!.*test_regress/!!; $_; } @args;
|
||||
|
||||
print "cd $ENV{PWD} && $FindBin::Bin/bootstrap.pl ",join(' ',@args),"\n";
|
||||
exec("./driver.pl", @args);
|
||||
die;
|
||||
|
||||
+25
-5
@@ -3,7 +3,14 @@
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2012 by Wilson Snyder.
|
||||
|
||||
module t;
|
||||
bit a_finished;
|
||||
bit b_finished;
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
wire [31:0] o;
|
||||
wire si = 1'b0;
|
||||
|
||||
@@ -18,6 +25,13 @@ module t;
|
||||
// Inputs
|
||||
.si (si));
|
||||
|
||||
always @ (posedge clk) begin
|
||||
if (!a_finished) $stop;
|
||||
if (!b_finished) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module InstModule (
|
||||
@@ -28,10 +42,7 @@ module InstModule (
|
||||
endmodule
|
||||
|
||||
program Prog (input si);
|
||||
initial begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
initial a_finished = 1'b1;
|
||||
endprogram
|
||||
|
||||
module ExampInst (o,i);
|
||||
@@ -55,3 +66,12 @@ module ExampInst (o,i);
|
||||
|
||||
endmodule
|
||||
|
||||
// Check bind at top level
|
||||
bind InstModule Prog2 instProg2
|
||||
(/*AUTOBIND*/
|
||||
.si (si));
|
||||
|
||||
// Check program declared after bind
|
||||
program Prog2 (input si);
|
||||
initial b_finished = 1'b1;
|
||||
endprogram
|
||||
|
||||
@@ -10,11 +10,14 @@ module t;
|
||||
|
||||
mc_t o;
|
||||
|
||||
logic [15:0] allones = 16'hffff;
|
||||
|
||||
initial begin
|
||||
if (4'shf > 4'sh0) $stop;
|
||||
if (signed'(4'hf) > 4'sh0) $stop;
|
||||
if (4'hf < 4'h0) $stop;
|
||||
if (unsigned'(4'shf) < 4'h0) $stop;
|
||||
if (4'(allones) !== 4'hf) $stop;
|
||||
|
||||
o = tocast_t'(4'b1);
|
||||
if (o != 4'b1) $stop;
|
||||
|
||||
@@ -17,7 +17,6 @@ compile (
|
||||
%Warning-CDCRSTLOGIC: See details in obj_dir/t_cdc_async_bad/Vt_cdc_async_bad__cdc.txt
|
||||
%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:\d+: Logic in path that feeds async reset, via signal: v.rst6a_bad_n
|
||||
%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:\d+: Logic in path that feeds async reset, via signal: v.rst6b_bad_n
|
||||
%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:\d+: Logic in path that feeds async reset, via signal: v.rst3_bad_n
|
||||
%Error: Exiting due to.*',
|
||||
);
|
||||
|
||||
|
||||
@@ -21,9 +21,8 @@ double sc_time_stamp () {
|
||||
VM_PREFIX* topp = NULL;
|
||||
|
||||
void clockit(int clk1, int clk0) {
|
||||
#ifdef T_CLK_2IN_VEC
|
||||
topp->clks = clk1<<1 | clk0;
|
||||
#else
|
||||
#ifndef T_CLK_2IN_VEC
|
||||
topp->c1 = clk1;
|
||||
topp->c0 = clk0;
|
||||
#endif
|
||||
@@ -38,6 +37,7 @@ int main (int argc, char *argv[]) {
|
||||
topp = new VM_PREFIX;
|
||||
topp->check = 0;
|
||||
clockit(0,0);
|
||||
main_time+=10;
|
||||
|
||||
Verilated::debug(0);
|
||||
|
||||
@@ -49,6 +49,9 @@ int main (int argc, char *argv[]) {
|
||||
clockit(1, 1);
|
||||
clockit(1, 0);
|
||||
clockit(0, 0);
|
||||
clockit(0, 1);
|
||||
clockit(1, 0);
|
||||
clockit(0, 0);
|
||||
}
|
||||
topp->check = 1;
|
||||
clockit(0,0);
|
||||
|
||||
@@ -7,18 +7,16 @@ 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} or $Self->skip("Verilator only test");
|
||||
|
||||
compile (
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
v_flags2 => ["$Self->{t_dir}/$Self->{name}.cpp"],
|
||||
verilator_flags2 => ["--exe"],
|
||||
);
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"],
|
||||
vcs_flags2 => ['-assert'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
||||
+111
-19
@@ -3,19 +3,75 @@
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2010 by Wilson Snyder.
|
||||
|
||||
module t (
|
||||
`ifdef T_CLK_2IN_VEC
|
||||
input [1:0] clks,
|
||||
`ifndef VERILATOR
|
||||
module t;
|
||||
/*AUTOREGINPUT*/
|
||||
// Beginning of automatic reg inputs (for undeclared instantiated-module inputs)
|
||||
reg c0; // To t2 of t2.v
|
||||
reg c1; // To t2 of t2.v
|
||||
reg check; // To t2 of t2.v
|
||||
reg [1:0] clks; // To t2 of t2.v
|
||||
// End of automatics
|
||||
t2 t2 (/*AUTOINST*/
|
||||
// Inputs
|
||||
.clks (clks[1:0]),
|
||||
.c0 (c0),
|
||||
.c1 (c1),
|
||||
.check (check));
|
||||
task clockit (input v1, v0);
|
||||
c1 = v1;
|
||||
c0 = v0;
|
||||
clks[1] = v1;
|
||||
clks[0] = v0;
|
||||
`ifdef TEST_VERBOSE $write("[%0t] c1=%x c0=%x\n", $time,v0,v1); `endif
|
||||
#1;
|
||||
endtask
|
||||
initial begin
|
||||
check = '0;
|
||||
c0 = '0;
|
||||
c1 = '0;
|
||||
clks = '0;
|
||||
#1
|
||||
t2.clear();
|
||||
#10;
|
||||
for (int i=0; i<2; i++) begin
|
||||
clockit(0, 0);
|
||||
clockit(0, 0);
|
||||
clockit(0, 1);
|
||||
clockit(1, 1);
|
||||
clockit(0, 0);
|
||||
clockit(1, 1);
|
||||
clockit(1, 0);
|
||||
clockit(0, 0);
|
||||
clockit(1, 0);
|
||||
clockit(0, 1);
|
||||
clockit(0, 0);
|
||||
end
|
||||
check = 1;
|
||||
clockit(0, 0);
|
||||
end
|
||||
endmodule
|
||||
`endif
|
||||
|
||||
`ifdef VERILATOR
|
||||
`define t2 t
|
||||
`else
|
||||
`define t2 t2
|
||||
`endif
|
||||
|
||||
module `t2 (
|
||||
input [1:0] clks,
|
||||
input c0,
|
||||
input c1,
|
||||
`endif
|
||||
input check
|
||||
);
|
||||
|
||||
`ifdef T_CLK_2IN_VEC
|
||||
wire c0 = clks[0];
|
||||
wire c1 = clks[1];
|
||||
wire clk0 = clks[0];
|
||||
wire clk1 = clks[1];
|
||||
`else
|
||||
wire clk0 = c0;
|
||||
wire clk1 = c1;
|
||||
`endif
|
||||
|
||||
integer p0 = 0;
|
||||
@@ -24,46 +80,82 @@ module t (
|
||||
integer n0 = 0;
|
||||
integer n1 = 0;
|
||||
integer n01 = 0;
|
||||
integer vp = 0;
|
||||
integer vn = 0;
|
||||
integer vpn = 0;
|
||||
task clear;
|
||||
`ifdef TEST_VERBOSE $display("[%0t] clear\n",$time); `endif
|
||||
p0 = 0;
|
||||
p1 = 0;
|
||||
p01 = 0;
|
||||
n0 = 0;
|
||||
n1 = 0;
|
||||
n01 = 0;
|
||||
vp = 0;
|
||||
vn = 0;
|
||||
vpn = 0;
|
||||
endtask
|
||||
|
||||
`define display_counts(text) begin \
|
||||
$write("[%0t] ",$time); \
|
||||
`ifdef T_CLK_2IN_VEC $write(" 2v "); `endif \
|
||||
$write(text); \
|
||||
$write(": %0d %0d %0d %0d %0d %0d\n", p0, p1, p01, n0, n1, n01); \
|
||||
$write(": %0d %0d %0d %0d %0d %0d %0d %0d %0d\n", p0, p1, p01, n0, n1, n01, vp, vn, vpn); \
|
||||
end
|
||||
|
||||
always @ (posedge c0) begin
|
||||
always @ (posedge clk0) begin
|
||||
p0 = p0 + 1; // Want blocking, so don't miss clock counts
|
||||
`ifdef TEST_VERBOSE `display_counts("posedge 0"); `endif
|
||||
end
|
||||
always @ (posedge c1) begin
|
||||
always @ (posedge clk1) begin
|
||||
p1 = p1 + 1;
|
||||
`ifdef TEST_VERBOSE `display_counts("posedge 1"); `endif
|
||||
end
|
||||
always @ (posedge c0 or posedge c1) begin
|
||||
always @ (posedge clk0 or posedge clk1) begin
|
||||
p01 = p01 + 1;
|
||||
`ifdef TEST_VERBOSE `display_counts("posedge *"); `endif
|
||||
end
|
||||
always @ (negedge c0) begin
|
||||
|
||||
always @ (negedge clk0) begin
|
||||
n0 = n0 + 1;
|
||||
`ifdef TEST_VERBOSE `display_counts("negedge 0"); `endif
|
||||
end
|
||||
always @ (negedge c1) begin
|
||||
always @ (negedge clk1) begin
|
||||
n1 = n1 + 1;
|
||||
`ifdef TEST_VERBOSE `display_counts("negedge 1"); `endif
|
||||
end
|
||||
always @ (negedge c0 or negedge c1) begin
|
||||
always @ (negedge clk0 or negedge clk1) begin
|
||||
n01 = n01 + 1;
|
||||
`ifdef TEST_VERBOSE `display_counts("negedge *"); `endif
|
||||
end
|
||||
|
||||
`ifndef VERILATOR
|
||||
always @ (posedge clks) begin
|
||||
vp = vp + 1;
|
||||
`ifdef TEST_VERBOSE `display_counts("pos vec"); `endif
|
||||
end
|
||||
always @ (negedge clks) begin
|
||||
vn = vn + 1;
|
||||
`ifdef TEST_VERBOSE `display_counts("neg vec"); `endif
|
||||
end
|
||||
always @ (posedge clks or negedge clks) begin
|
||||
vpn = vpn + 1;
|
||||
`ifdef TEST_VERBOSE `display_counts("or vec"); `endif
|
||||
end
|
||||
`endif
|
||||
|
||||
always @ (posedge check) begin
|
||||
if (p0!=4) $stop;
|
||||
if (p1!=4) $stop;
|
||||
if (p01!=6) $stop;
|
||||
if (n0!=4) $stop;
|
||||
if (n1!=4) $stop;
|
||||
if (n01!=6) $stop;
|
||||
if (p0!=6) $stop;
|
||||
if (p1!=6) $stop;
|
||||
if (p01!=10) $stop;
|
||||
if (n0!=6) $stop;
|
||||
if (n1!=6) $stop;
|
||||
if (n01!=10) $stop;
|
||||
`ifndef VERILATOR
|
||||
if (vp!=6) $stop;
|
||||
if (vn!=6) $stop;
|
||||
if (vpn!=12) $stop;
|
||||
`endif
|
||||
$write("*-* All Finished *-*\n");
|
||||
end
|
||||
|
||||
|
||||
@@ -9,13 +9,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
||||
|
||||
top_filename("t/t_clk_2in.v");
|
||||
|
||||
$Self->{vlt} or $Self->skip("Verilator only test");
|
||||
|
||||
compile (
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
v_flags2 => ["+define+T_CLK_2IN_VEC=1 $Self->{t_dir}/t_clk_2in.cpp"],
|
||||
verilator_flags2 => ["--exe"],
|
||||
v_flags2 => ["+define+T_CLK_2IN_VEC=1"],
|
||||
verilator_flags2 => ["--exe $Self->{t_dir}/t_clk_2in.cpp"],
|
||||
);
|
||||
|
||||
execute (
|
||||
|
||||
@@ -7,14 +7,11 @@ 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.
|
||||
|
||||
my $fail = ($Self->{v3} && verilator_version() !~ /\(ord\)/);
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished => !$fail,
|
||||
fails => $fail,
|
||||
check_finished => 1
|
||||
);
|
||||
|
||||
ok(1);
|
||||
|
||||
@@ -7,10 +7,12 @@ 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, bug102");
|
||||
|
||||
compile (
|
||||
v_flags => ["--lint-only"]
|
||||
verilator_flags2=>["-no-order-clock-delay"],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished => 1
|
||||
);
|
||||
|
||||
ok(1);
|
||||
@@ -0,0 +1,131 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2005 by Wilson Snyder.
|
||||
|
||||
module t (clk);
|
||||
input clk;
|
||||
|
||||
reg [0:0] d1;
|
||||
reg [2:0] d3;
|
||||
reg [7:0] d8;
|
||||
|
||||
wire [0:0] q1;
|
||||
wire [2:0] q3;
|
||||
wire [7:0] q8;
|
||||
|
||||
// verilator lint_off UNOPTFLAT
|
||||
reg ena;
|
||||
// verilator lint_on UNOPTFLAT
|
||||
|
||||
condff #(12) condff
|
||||
(.clk(clk), .sen(1'b0), .ena(ena),
|
||||
.d({d8,d3,d1}),
|
||||
.q({q8,q3,q1}));
|
||||
|
||||
integer cyc; initial cyc=1;
|
||||
always @ (posedge clk) begin
|
||||
if (cyc!=0) begin
|
||||
//$write("%x %x %x %x\n", cyc, q8, q3, q1);
|
||||
cyc <= cyc + 1;
|
||||
if (cyc==1) begin
|
||||
d1 <= 1'b1; d3<=3'h1; d8<=8'h11;
|
||||
ena <= 1'b1;
|
||||
end
|
||||
if (cyc==2) begin
|
||||
d1 <= 1'b0; d3<=3'h2; d8<=8'h33;
|
||||
ena <= 1'b0;
|
||||
end
|
||||
if (cyc==3) begin
|
||||
d1 <= 1'b1; d3<=3'h3; d8<=8'h44;
|
||||
ena <= 1'b1;
|
||||
// PROPER ANSWER is 8'h11, but we are negative-testing
|
||||
//if (q8 != 8'h11) $stop;
|
||||
if (q8 != 8'h33) $stop;
|
||||
end
|
||||
if (cyc==4) begin
|
||||
d1 <= 1'b1; d3<=3'h4; d8<=8'h77;
|
||||
ena <= 1'b1;
|
||||
// PROPER ANSWER is 8'h11, but we are negative-testing
|
||||
//if (q8 != 8'h11) $stop;
|
||||
if (q8 != 8'h33) $stop;
|
||||
end
|
||||
if (cyc==5) begin
|
||||
d1 <= 1'b1; d3<=3'h0; d8<=8'h88;
|
||||
ena <= 1'b1;
|
||||
// PROPER ANSWER is 8'h44, but we are negative-testing
|
||||
//if (q8 != 8'h44) $stop;
|
||||
end
|
||||
if (cyc==6) begin
|
||||
// PROPER ANSWER is 8'h77, but we are negative-testing
|
||||
//if (q8 != 8'h77) $stop;
|
||||
end
|
||||
if (cyc==7) begin
|
||||
// PROPER ANSWER is 8'h88, but we are negative-testing
|
||||
//if (q8 != 8'h88) $stop;
|
||||
end
|
||||
//
|
||||
if (cyc==20) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module condff (clk, sen, ena, d, q);
|
||||
parameter WIDTH = 1;
|
||||
input clk;
|
||||
|
||||
input sen;
|
||||
input ena;
|
||||
input [WIDTH-1:0] d;
|
||||
output [WIDTH-1:0] q;
|
||||
|
||||
condffimp #(.WIDTH(WIDTH))
|
||||
imp (.clk(clk), .sen(sen), .ena(ena), .d(d), .q(q));
|
||||
endmodule
|
||||
|
||||
module condffimp (clk, sen, ena, d, q);
|
||||
parameter WIDTH = 1;
|
||||
input clk;
|
||||
input sen;
|
||||
input ena;
|
||||
input [WIDTH-1:0] d;
|
||||
output reg [WIDTH-1:0] q;
|
||||
wire gatedclk;
|
||||
|
||||
clockgate clockgate (.clk(clk), .sen(sen), .ena(ena), .gatedclk(gatedclk));
|
||||
|
||||
always @(posedge gatedclk) begin
|
||||
if (gatedclk === 1'bX) begin
|
||||
q <= {WIDTH{1'bX}};
|
||||
end
|
||||
else begin
|
||||
q <= d;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module clockgate (clk, sen, ena, gatedclk);
|
||||
input clk;
|
||||
input sen;
|
||||
input ena;
|
||||
output gatedclk;
|
||||
|
||||
reg ena_b;
|
||||
wire gatedclk = clk & ena_b;
|
||||
|
||||
// verilator lint_off COMBDLY
|
||||
always @(clk or ena or sen) begin
|
||||
if (~clk) begin
|
||||
ena_b <= ena | sen;
|
||||
end
|
||||
else begin
|
||||
if ((clk^sen)===1'bX) ena_b <= 1'bX;
|
||||
end
|
||||
end
|
||||
// verilator lint_on COMBDLY
|
||||
|
||||
endmodule
|
||||
@@ -7,14 +7,11 @@ 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.
|
||||
|
||||
my $fail = ($Self->{v3} && verilator_version() !~ /\(ord\)/);
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished => !$fail,
|
||||
fails => $fail,
|
||||
check_finished => 1
|
||||
);
|
||||
|
||||
ok(1);
|
||||
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/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 (
|
||||
verilator_flags2 => ["--stats"],
|
||||
);
|
||||
|
||||
if ($Self->{vlt}) {
|
||||
file_grep ($Self->{stats}, qr/Optimizations, Gate sigs deduped\s+(\d+)/i, 4);
|
||||
}
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,61 @@
|
||||
// DESCRIPTION: Verilator: Dedupe optimization test.
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty.
|
||||
|
||||
// Contributed 2012 by Varun Koyyalagunta, Centaur Technology.
|
||||
|
||||
module t(res,d,clk,en);
|
||||
output res;
|
||||
input d,en,clk;
|
||||
wire q0,q1,q2,q3;
|
||||
|
||||
flop_gated_latch f0(q0,d,clk,en);
|
||||
flop_gated_latch f1(q1,d,clk,en);
|
||||
flop_gated_flop f2(q2,d,clk,en);
|
||||
flop_gated_flop f3(q3,d,clk,en);
|
||||
assign res = (q0 + q1) * (q2 - q3);
|
||||
endmodule
|
||||
|
||||
module flop_gated_latch(q,d,clk,en);
|
||||
input d, clk, en;
|
||||
output q;
|
||||
wire gated_clock;
|
||||
clock_gate_latch clock_gate(gated_clock, clk, en);
|
||||
always @(posedge gated_clock) begin
|
||||
q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module flop_gated_flop(q,d,clk,en);
|
||||
input d, clk, en;
|
||||
output q;
|
||||
wire gated_clock;
|
||||
clock_gate_flop clock_gate(gated_clock, clk, en);
|
||||
always @(posedge gated_clock) begin
|
||||
q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module clock_gate_latch (gated_clk, clk, clken);
|
||||
output gated_clk;
|
||||
input clk, clken;
|
||||
reg clken_latched /*verilator clock_enable*/;
|
||||
assign gated_clk = clk & clken_latched ;
|
||||
|
||||
wire clkb = ~clk;
|
||||
always @(clkb or clken)
|
||||
if(clkb) clken_latched = clken;
|
||||
|
||||
endmodule
|
||||
|
||||
module clock_gate_flop (gated_clk, clk, clken);
|
||||
output gated_clk;
|
||||
input clk, clken;
|
||||
reg clken_r /*verilator clock_enable*/;
|
||||
assign gated_clk = clk & clken_r ;
|
||||
|
||||
always @(negedge clk)
|
||||
clken_r <= clken;
|
||||
|
||||
endmodule
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/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 (
|
||||
verilator_flags2 => ["--stats"],
|
||||
);
|
||||
|
||||
if ($Self->{vlt}) {
|
||||
file_grep ($Self->{stats}, qr/Optimizations, Gate sigs deduped\s+(\d+)/i, 6);
|
||||
}
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,123 @@
|
||||
// DESCRIPTION: Verilator: Dedupe optimization test.
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty.
|
||||
|
||||
// Contributed 2012 by Varun Koyyalagunta, Centaur Technology.
|
||||
//
|
||||
// Test consists of the follow logic tree, which has many obvious
|
||||
// places for dedupe:
|
||||
/*
|
||||
output
|
||||
+
|
||||
--------------/ \--------------
|
||||
/ \
|
||||
+ +
|
||||
----/ \----- ----/ \----
|
||||
/ + / +
|
||||
+ / \ + / \
|
||||
-/ \- a b -/ \- a b
|
||||
/ \ / \
|
||||
+ + + +
|
||||
/ \ / \ / \ / \
|
||||
a b c d a b c d
|
||||
*/
|
||||
|
||||
module t(sum,a,b,c,d,clk);
|
||||
output sum;
|
||||
input a,b,c,d,clk;
|
||||
wire left,right;
|
||||
add add(sum,left,right,clk);
|
||||
l l(left,a,b,c,d,clk);
|
||||
r r(right,a,b,c,d,clk);
|
||||
endmodule
|
||||
|
||||
module l(sum,a,b,c,d,clk);
|
||||
output sum;
|
||||
input a,b,c,d,clk;
|
||||
wire left, right;
|
||||
add add(sum,left,right,clk);
|
||||
ll ll(left,a,b,c,d,clk);
|
||||
lr lr(right,a,b,c,d,clk);
|
||||
endmodule
|
||||
|
||||
module ll(sum,a,b,c,d,clk);
|
||||
output sum;
|
||||
input a,b,c,d,clk;
|
||||
wire left, right;
|
||||
add add(sum,left,right,clk);
|
||||
lll lll(left,a,b,c,d,clk);
|
||||
llr llr(right,a,b,c,d,clk);
|
||||
endmodule
|
||||
|
||||
module lll(sum,a,b,c,d,clk);
|
||||
output sum;
|
||||
input a,b,c,d,clk;
|
||||
add add(sum,a,b,clk);
|
||||
endmodule
|
||||
|
||||
module llr(sum,a,b,c,d,clk);
|
||||
output sum;
|
||||
input a,b,c,d,clk;
|
||||
add add(sum,c,d,clk);
|
||||
endmodule
|
||||
|
||||
module lr(sum,a,b,c,d,clk);
|
||||
output sum;
|
||||
input a,b,c,d,clk;
|
||||
add add(sum,a,b,clk);
|
||||
endmodule
|
||||
|
||||
module r(sum,a,b,c,d,clk);
|
||||
output sum;
|
||||
input a,b,c,d,clk;
|
||||
wire left, right;
|
||||
add add(sum,left,right,clk);
|
||||
rl rl(left,a,b,c,d,clk);
|
||||
rr rr(right,a,b,c,d,clk);
|
||||
endmodule
|
||||
|
||||
module rr(sum,a,b,c,d,clk);
|
||||
output sum;
|
||||
input a,b,c,d,clk;
|
||||
add add(sum,a,b,clk);
|
||||
endmodule
|
||||
|
||||
module rl(sum,a,b,c,d,clk);
|
||||
output sum;
|
||||
input a,b,c,d,clk;
|
||||
wire left, right;
|
||||
add add(sum,left,right,clk);
|
||||
rll rll(left,a,b,c,d,clk);
|
||||
rlr rlr(right,a,b,c,d,clk);
|
||||
endmodule
|
||||
|
||||
module rll(sum,a,b,c,d,clk);
|
||||
output sum;
|
||||
input a,b,c,d,clk;
|
||||
add2 add(sum,a,b,clk);
|
||||
endmodule
|
||||
|
||||
module rlr(sum,a,b,c,d,clk);
|
||||
output sum;
|
||||
input a,b,c,d,clk;
|
||||
add2 add(sum,c,d,clk);
|
||||
endmodule
|
||||
|
||||
module add(sum,x,y,clk);
|
||||
output sum;
|
||||
input x,y,clk;
|
||||
reg t1,t2;
|
||||
always @(posedge clk) begin
|
||||
sum <= x + y;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module add2(sum,x,y,clk);
|
||||
output sum;
|
||||
input x,y,clk;
|
||||
reg t1,t2;
|
||||
always @(posedge clk) begin
|
||||
sum <= x + y;
|
||||
end
|
||||
endmodule
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/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 (
|
||||
verilator_flags2 => ["-Wno-UNOPTFLAT"]
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,41 @@
|
||||
// DESCRIPTION: Verilator: Simple test of unoptflat
|
||||
//
|
||||
// Trigger the DETECTARRAY error.
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2013 by Jeremy Bennett.
|
||||
|
||||
localparam ID_MSB = 1;
|
||||
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
typedef struct packed {
|
||||
logic [ID_MSB:0] id;
|
||||
} context_t;
|
||||
|
||||
context_t tsb;
|
||||
|
||||
assign tsb.id = {tsb.id[0], clk};
|
||||
|
||||
initial begin
|
||||
tsb.id = 0;
|
||||
end
|
||||
|
||||
always @(posedge clk or negedge clk) begin
|
||||
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("tsb.id = %x\n", tsb.id);
|
||||
`endif
|
||||
|
||||
if (tsb.id[1] != 0) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/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 (
|
||||
verilator_flags2 => ["-Wno-UNOPTFLAT"]
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
@@ -0,0 +1,43 @@
|
||||
// DESCRIPTION: Verilator: Simple test of unoptflat
|
||||
//
|
||||
// This should trigger the DETECTARRAY error like t_detectarray_1.v, but in
|
||||
// fact it casuses a broken link error. The only difference is that the struct
|
||||
// is defined using a constant rather than a localparam.
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2013 by Jeremy Bennett.
|
||||
|
||||
localparam ID_MSB = 1;
|
||||
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
typedef struct packed {
|
||||
logic [1:0] id;
|
||||
} context_t;
|
||||
|
||||
context_t tsb;
|
||||
|
||||
assign tsb.id = {tsb.id[0], clk};
|
||||
|
||||
initial begin
|
||||
tsb.id = 0;
|
||||
end
|
||||
|
||||
always @(posedge clk or negedge clk) begin
|
||||
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("tsb.id = %x\n", tsb.id);
|
||||
`endif
|
||||
|
||||
if (tsb.id[1] != 0) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -38,10 +38,11 @@ if (!-r "$root/.git") {
|
||||
# Check empty
|
||||
my @files;
|
||||
$finds = `find $destdir -type f -print`;
|
||||
foreach my $f (split /\n/, $finds) {
|
||||
print "\tLEFT: $f\n";
|
||||
$f =~ s!^$cwd!.!;
|
||||
push @files, $f;
|
||||
foreach my $file (split /\n/, $finds) {
|
||||
next if $file =~ /\.status/; # Made by driver.pl, not Verilator
|
||||
print "\tLEFT: $file\n";
|
||||
$file =~ s!^$cwd!.!;
|
||||
push @files, $file;
|
||||
}
|
||||
if ($#files >= 0) {
|
||||
$Self->error("Uninstall missed files: ",join(' ',@files));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user