Compare commits

...
92 Commits
Author SHA1 Message Date
Wilson Snyder f1ef30a5bb Version bump 2013-09-29 20:52:29 -04:00
Wilson Snyder f1d9437c55 Report SELRANGE warning for non-generate if, bug675. 2013-09-10 07:16:13 -04:00
Wilson Snyder bcba5075e8 Fix ordering of , msg1229. 2013-09-07 16:43:43 -04:00
Wilson Snyder d6e8b0263c Fix crash on 32-bit Ubuntu, bug670. 2013-09-03 21:40:43 -04:00
Wilson Snyder 9aba617bad Fix --output-split-cfunc to count internal functions. 2013-09-03 19:35:32 -04:00
Wilson Snyder c24f7b1391 Support named function and task arguments. 2013-08-17 20:34:49 -04:00
Wilson Snyder d4e27b635f devel release 2013-08-15 08:43:37 -04:00
Wilson Snyder c996d2870c Version bump 2013-08-15 08:39:46 -04:00
Wilson Snyder bbd59f8a22 Support passing strings to DPI imports. 2013-08-14 21:37:13 -04:00
Wilson Snyder d3d359e757 Fix clang warnings, bug668. 2013-08-08 19:39:39 -04:00
Wilson Snyder ae763ea93d Internals: Debugs for bug666. 2013-08-08 07:05:21 -04:00
Wilson Snyder 236b9e9761 Fix parameter real conversion from integer. 2013-07-29 22:03:47 -04:00
Wilson Snyder e42c9dfd84 Grammar 2013-07-29 21:53:43 -04:00
Wilson Snyder bebf5b291b Fix final duplicate declarations when non-inlined, bug661. 2013-07-29 21:47:23 -04:00
Wilson Snyder 1e3dcd203d Fix clang warning, bug668. 2013-07-29 10:37:58 -04:00
Wilson Snyder cc7da63dec Tests: Add t_mod_recurse. 2013-06-23 22:39:08 -04:00
Wilson Snyder 1baa2a2558 Fix interface ports with comma lists, msg1058. 2013-06-13 19:38:18 -04:00
Wilson Snyder e63ff77b15 VPI internals: Rename VerilatedRange left/right to match IEEE + VNumRange. No functional change. 2013-06-13 08:05:38 -04:00
Wilson Snyder 6cf9468477 Fix vpi_iterate on memory words, bug655. 2013-06-13 07:58:52 -04:00
Jeremy Bennett b277bc8750 Fix ordering of clock enables with delayed assigns, bug613.
Signed-off-by: Wilson Snyder <[email protected]>
2013-06-05 23:35:47 -04:00
Wilson Snyder ba9da64207 Fix t_trace_cat test, bug654. 2013-06-03 19:08:15 -04:00
Wilson Snyder 9cb9f9c80b devel release 2013-06-02 14:52:19 -04:00
Wilson Snyder 7a65df7636 Version bump 2013-06-02 14:47:36 -04:00
Wilson Snyder eab46d547d Version bump. 2013-06-02 14:38:53 -04:00
Wilson Snyder 3dd552c4a2 Duplicate clock gate optimization on by default, use -Od to disable 2013-05-27 22:39:59 -04:00
Wilson Snyder 23bb045a72 Support interfaces and modports, bug102. 2013-05-27 21:39:19 -04:00
Wilson Snyder 7c834ad118 Internals: Misc cleanups from interface branch. No functional change. 2013-05-27 20:56:20 -04:00
Wilson Snyder 8e2617ab8d Internals: V3Inline support for future hard-no-inline. No functional change. 2013-05-26 11:17:42 -04:00
Wilson Snyder 24fcae4f49 Internals: When broken link fatal, say which rule violated 2013-05-25 17:05:22 -04:00
Wilson Snyder ce18674d88 Internals: (see last) also dump on first non-fatal 2013-05-25 13:31:17 -04:00
Wilson Snyder 6b8d9b5c36 Internals: If registered, dump symtable on any error 2013-05-25 12:15:38 -04:00
Wilson Snyder 81bf95763c Internals: Refactor V3Inline to simplify if(m_cellp) out. No functional change. 2013-05-25 10:42:44 -04:00
Wilson Snyder 2d64077fd0 Internals: Prep to allow future move of wrapping. No functional change. 2013-05-25 10:15:10 -04:00
Wilson Snyder 38faebcf20 Fix packed array select internal error, bug652. 2013-05-24 21:32:54 -04:00
Wilson Snyder 48d177a9d0 Fix packed array select internal error, bug652. 2013-05-24 21:14:42 -04:00
Wilson Snyder 3b3a7f7354 Commentary 2013-05-24 19:09:41 -04:00
Wilson Snyder 99cd2818a1 Tests: Allow driver to find 't' signals when not first in file. 2013-05-23 22:00:55 -04:00
Wilson Snyder 5765e099a6 Debug: More digits for debug file sorting. 2013-05-23 20:50:48 -04:00
Wilson Snyder 175d59ecba Fix GCC version runtime changes, bug651. 2013-05-23 20:19:51 -04:00
Wilson Snyder 3bd3d01968 Tests: Move driver.pl tee into perl to avoid process issue, bug650. 2013-05-23 20:08:39 -04:00
Wilson Snyder 85e2a6bb45 Tests 2013-05-21 22:38:35 -04:00
Wilson Snyder 84efd239a5 Fix arrayed input compile error, bug645. Try 2. 2013-05-18 20:17:17 -04:00
Wilson Snyder 6a69813326 Internals: Renames for interfaces. No functional change. 2013-05-18 19:45:52 -04:00
Wilson Snyder 2c9dcc3913 Fix arrayed input compile error, bug645. 2013-05-15 22:00:28 -04:00
Wilson Snyder 20ea1ca018 devel release 2013-05-11 16:16:22 -04:00
Wilson Snyder 0abde90933 Version bump 2013-05-11 16:11:38 -04:00
Wilson Snyder e01b69198a driver: Fix if no forker. 2013-05-11 16:11:26 -04:00
Wilson Snyder 53cd9d2403 Fix nested union crash, bug643. 2013-05-10 21:02:48 -04:00
Wilson Snyder 3d0f5fc078 Fix packed array non-zero right index select crash, bug642. 2013-05-10 07:09:25 -04:00
Wilson Snyder 54eedcc739 Support signal[vec]++. 2013-05-06 08:02:16 -04:00
Wilson Snyder ae6f5844da Add Verilated::internalsDump() 2013-05-04 10:29:54 -04:00
Wilson Snyder 1bea845ceb Fix simulation error when inputs and MULTIDRIVEN, bug634. 2013-05-02 08:23:17 -04:00
Wilson Snyder d581582339 Add ALWCOMBORDER warning. 2013-04-30 22:55:28 -04:00
Wilson Snyder 4eabc1992e Fix gcc 4.1.2 compile warnings 2013-04-30 22:55:03 -04:00
Wilson Snyder 316eb24bbb Example test 2013-04-28 18:58:04 -04:00
Jeremy Bennett f0a8824efc Commentary, bug639.
Signed-off-by: Wilson Snyder <[email protected]>
2013-04-28 09:04:04 -04:00
Wilson Snyder 345a5d5646 Add --pins-sc-uint and --pins-sc-biguint, bug638. 2013-04-26 21:02:32 -04:00
Wilson Snyder 464679c78b Fix module resolution with __, bug631. 2013-03-12 07:27:17 -04:00
Wilson Snyder 28eeec1cf4 devel release 2013-03-09 16:48:10 -05:00
Wilson Snyder 7d0dce3267 Version bump 2013-03-09 16:44:48 -05:00
Wilson Snyder 9e29625207 Fix UNOPTFLAT circular array bounds crossing, bug630. 2013-03-08 19:25:20 -05:00
Wilson Snyder a767da4f3f Support <number>'() sized casts, bug628. 2013-03-05 22:13:22 -05:00
Wilson Snyder 7bd96c2876 Internals: Tristate commentary 2013-02-27 22:59:17 -05:00
Wilson Snyder 70fd64dcd6 IEEE 1800-2012 is now the default language. This adds 4 new keywords and updates the svdpi.h and vpi_user.h header files. 2013-02-26 23:01:19 -05:00
Jeremy Bennett bb2822f4b5 Add --report-unoptflat, bug611.
Signed-off-by: Wilson Snyder <[email protected]>
2013-02-26 22:26:47 -05:00
Wilson Snyder ad21108b63 Internals: Create graph clone methods. 2013-02-25 21:03:50 -05:00
Wilson Snyder e6808a787c Fix opening a VerilatedVcdC file multiple times, msg1021. 2013-02-23 21:10:25 -05:00
Wilson Snyder 1fb2762725 Tests: Clocking test to consider bug623. 2013-02-22 21:00:37 -05:00
Wilson Snyder cce55941d3 Makefile: Distribute .gdbinit 2013-02-22 17:24:15 -05:00
Wilson Snyder 6c8d95e0e2 Nice message on fopen with missing argument. 2013-02-22 17:14:27 -05:00
Wilson Snyder 216a178b28 Makefile: Distribute .gdbinit 2013-02-22 17:13:31 -05:00
Wilson Snyder 6594a54a95 Fix wrong dot resolution under inlining. 2013-02-21 23:38:29 -05:00
Wilson Snyder a9a4cf061a Fix tristate duplicate __Vcellinp declaration 2013-02-20 22:28:56 -05:00
Wilson Snyder b7f0e204cb Spelling fixes 2013-02-20 21:51:39 -05:00
Varun Koyyalagunta e6a15f233b Internals: GateDedupe: Use visitor per msg980.
Signed-off-by: Wilson Snyder <[email protected]>
2013-02-20 20:26:53 -05:00
Varun Koyyalagunta e0edb596ea Add duplicate clock gate optimization, msg980.
Experimental and disabled unless -OD or -O3 used (for now),
Please try it as may get some significant speedups.

Signed-off-by: Wilson Snyder <[email protected]>
2013-02-20 20:14:15 -05:00
Varun Koyyalagunta f2fb77c15a Internals: New Hashed/Graph functions towards msg980.
Signed-off-by: Wilson Snyder <[email protected]>
2013-02-19 18:49:36 -05:00
Wilson Snyder 772a3a97eb Internals: Functions in order. No functional change. 2013-02-18 12:15:50 -05:00
Wilson Snyder 6c310836a1 Internals: Track original signal name. No functional change. 2013-02-18 11:22:24 -05:00
Wilson Snyder 75416a3016 Commentary 2013-02-18 11:05:47 -05:00
Wilson Snyder ebef78a13e Tests 2013-02-18 10:17:34 -05:00
Wilson Snyder e71baca39b Internals: Make propagateAttrClocksFrom. No functional change. 2013-02-16 08:07:18 -05:00
Wilson Snyder 18eb210313 Support bind in , bug602. 2013-02-14 06:55:09 -05:00
Wilson Snyder 7d38a5e1f9 Test bug424. 2013-02-13 21:10:17 -05:00
Wilson Snyder 4386077e2d Support pattern assignments with data type labels, bug618. 2013-02-13 20:52:38 -05:00
Wilson Snyder 49dbfd2131 Support pattern assignments in function calls, bug617. 2013-02-13 20:32:25 -05:00
Wilson Snyder a80fce5ac1 Support pattern assignments to const variables, bug616. 2013-02-13 19:32:36 -05:00
Wilson Snyder 891b981cab Fix LITENDIAN on unpacked structures, bug614. 2013-02-13 19:03:10 -05:00
Rich Porter 2dd87b8384 Fix 32-bit OS VPI scan issue, bug615.
Signed-off-by: Wilson Snyder <[email protected]>
2013-02-11 07:17:18 -05:00
Jeremy Bennett 062eb85075 Fix DETECTARRAY on packed structures, bug610.
Signed-off-by: Wilson Snyder <[email protected]>
2013-02-10 09:54:27 -05:00
Wilson Snyder e1f6802f5f tests: Fix bootstrap wouldn't cd for test_regress/t/t_dist_portability.pl. 2013-02-10 09:29:35 -05:00
Wilson Snyder 6cfcd50f12 devel release 2013-02-04 22:19:37 -05:00
236 changed files with 10937 additions and 1179 deletions
+88
View File
@@ -3,6 +3,94 @@ 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.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
View File
@@ -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
+1
View File
@@ -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 \
+94 -24
View File
@@ -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
@@ -299,8 +300,11 @@ descriptions in the next sections for more information.
-O<optimization-letter> Selectable optimizations
-o <executable> Name of final executable
--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 +312,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 +378,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 +591,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 +604,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>
@@ -804,6 +811,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 +828,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 +903,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 +965,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 +1074,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 +1106,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 +1926,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 +2607,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 +2723,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 +2904,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 +2991,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 +3210,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
View File
@@ -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.852 2013-09-29])
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
View File
@@ -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
View File
@@ -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);
}
+4
View File
@@ -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
View File
@@ -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.
+10 -8
View File
@@ -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() {}
};
+9 -2
View File
@@ -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) {
+7 -1
View File
@@ -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
View File
@@ -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++) {
+5 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1
View File
@@ -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
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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();
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+6
View File
@@ -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
View File
@@ -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
View File
@@ -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");
+12 -3
View File
@@ -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
}
@@ -736,11 +739,14 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
else if ( onoff (sw, "-lint-only", flag/*ref*/) ) { m_lintOnly = flag; }
else if ( !strcmp (sw, "-no-pins64") ) { m_pinsBv = 33; }
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 +771,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;
@@ -1212,6 +1219,7 @@ V3Options::V3Options() {
m_traceDups = false;
m_traceUnderscore = false;
m_underlineZero = false;
m_reportUnoptflat = false;
m_xInitialEdge = false;
m_xmlOnly = false;
@@ -1301,6 +1309,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
+8
View File
@@ -76,6 +76,8 @@ class V3Options {
bool m_lintOnly; // main switch: --lint-only
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 +91,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 +134,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
@@ -213,6 +217,8 @@ class V3Options {
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 +227,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 +273,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; }
+177 -18
View File
@@ -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,21 @@ 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 (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 +1342,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 +1753,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 +1767,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
View File
@@ -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
View File
@@ -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"));
}
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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
+3
View File
@@ -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;
-1
View File
@@ -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.*',
);
+5 -2
View File
@@ -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 -9
View File
@@ -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
View File
@@ -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
+2 -4
View File
@@ -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 (
+1 -4
View File
@@ -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);
+1 -4
View File
@@ -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);
+19
View File
@@ -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;
+61
View File
@@ -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
+19
View File
@@ -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;
+123
View File
@@ -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
+19
View File
@@ -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;
+41
View File
@@ -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
+19
View File
@@ -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;
+43
View File
@@ -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
+5 -4
View File
@@ -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));
+4 -1
View File
@@ -54,9 +54,12 @@ sub printfll {
my %names;
foreach my $line (split /\n/, $grep) {
next if $line !~ /%[a-z0-9]*ll/;
next if $line !~ /\blong\S+long\b/; # Assume a cast
print "$line\n";
if ($line =~ /^([^:]+)/) {
$names{$1} = 1;
print "$line\n";
} else {
$names{UNKNOWN} = 1;
}
}
if (keys %names) {
+1 -1
View File
@@ -99,7 +99,7 @@ int dpix_run_tests() {
static int didDump = 0;
if (didDump++ == 0) {
# ifdef TEST_VERBOSE
Verilated::scopesDump();
Verilated::internalsDump();
# endif
}
#endif

Some files were not shown because too many files have changed in this diff Show More