Version bump

git-svn-id: file://localhost/svn/verilator/trunk/verilator@753 77ca24e4-aefa-0310-84f0-b9a241c72d87
This commit is contained in:
Wilson Snyder 2006-08-26 11:35:28 +00:00
commit ce10dbd11c
515 changed files with 66145 additions and 0 deletions

20
.cvsignore Normal file
View File

@ -0,0 +1,20 @@
*.old
*.gz
*.gz.uu
*.html
*.info
*.log
*.1
*.tmp
*.tex
Makefile
README
config.cache
config.status
configure
dddrun*
gdbrun*
verilator.txt
verilator.pdf
verilator_bin*
autom4te.cache

339
COPYING Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

914
Changes Normal file
View File

@ -0,0 +1,914 @@
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.600 08/26/2006
** Support dotted cross-hierarchy variable and task references.
**** Lint for x's in generate case statements.
**** Fix line numbers being off by one when first file starts with newline.
**** Fix naming of generate for blocks to prevent non-inline name conflict.
* Verilator 3.542 08/11/2006 Stable
**** Fix extraneous UNSIGNED warning when comparing genvars. [David Hewson]
**** Fix extra whitespace in $display %c. [by David Addison]
**** vl_finish and vl_fatal now print via VL_PRINTF rather then cerr/cout.
**** Add VL_CONST_W_24X macro. [Bernard Deadman]
* Verilator 3.541 07/05/2006 Beta
*** Fix "// verilator lint_on" not re-enabling warnings. [David Hewson]
*** Fix 3.540's multiple memory assignments to same block. [David Hewson]
**** Add warning on changeDetect to arrayed structures. [David Hewson]
**** Fix non-zero start number for arrayed instantiations. [Jae Hossell]
**** Fix GCC 4.0 header file warnings.
* Verilator 3.540 06/27/2006 Beta
**** Optimize combo assignments that are used only once, ~5-25% faster.
**** Optimize delayed assignments to memories inside loops, ~0-5% faster.
**** Fix mis-width warning on bit selects of memories. [David Hewson]
**** Fix mis-width warning on dead generate-if branches. [Jae Hossell]
* Verilator 3.533 06/05/2006 Stable
*** Add PDF user manual, verilator.pdf.
**** Fix delayed bit-selected arrayed assignments. [David Hewson]
**** Fix execution path to Perl. [Shanshan Xu]
**** Fix Bison compile errors in verilog.y. [by Ben Jackson]
* Verilator 3.531 05/10/2006 Stable
*** Support $c routines which return 64 bit values.
**** Fix `include `DEFINE.
**** Fix Verilator core dump when have empty public function. [David.Hewson]
* Verilator 3.530 04/24/2006 Stable
** $time is now 64 bits. The macro VL_TIME_I is now VL_TIME_Q, but calls
the same sc_time_stamp() function to get the current time.
* Verilator 3.523 03/06/2006 Stable
**** Fix error line numbers being off due to multi-line defines. [Mat Zeno]
**** Fix GCC sign extending (uint64_t)(a<b). [David Hewson]
**** Fix `systemc_imp_header "undefined macro" error.
* Verilator 3.522 02/23/2006 Beta
**** Add UNUSED error message, for forward compatibility.
* Verilator 3.521 02/14/2006 Beta
*** Create new --coverage-line and --coverage-user options. [Peter Holmes]
**** Added SystemVerilog 'x,'z,'0,'1, and new string literals.
**** Fix public module's parent still getting inlined.
* Verilator 3.520 01/14/2006 Stable
** Added support for $fopen, $fclose, $fwrite, $fdisplay.
See documentation, as the file descriptors differ from the standard.
* Verilator 3.510 12/17/2005 Stable
** Improve trace-on performance on large multi-clock designs by 2x or more.
This adds a small ~2% performance penalty if traces are compiled in,
but not turned on. For best non-tracing performance, do not use --trace.
**** Fix $'s in specify delays causing bad PLI errors. [Mat Zeno]
**** Fix public functions not setting up proper symbol table. [Mat Zeno]
**** Fix genvars generating trace compile errors. [Mat Zeno]
**** Fix VL_MULS_WWW compile error with MSVC++. [Wim Michiels]
* Verilator 3.502 11/30/2005 Stable
**** Fix local non-IO variables in public functions and tasks.
**** Fix bad lifetime optimization when same signal is assigned multiple
times in both branch of a if. [Danny Ding]
* Verilator 3.501 11/16/2005 Stable
*** Add --profile-cfuncs for correlating profiles back to Verilog.
**** Fix functions where regs are declared before inputs. [Danny Ding]
**** Fix bad deep expressions with bitselects and rotate. [Prabhat Gupta]
* Verilator 3.500 10/30/2005 Stable
** Support signed numbers, >>>, $signed, $unsigned. [MANY!]
** Support multi-dimensional arrays. [Eugen Fekete]
** Add very limited support for the Property Specification Language
(aka PSL or Sugar). The format and keywords are now very limited, but will
grow with future releases. The --assert switch enables this feature.
** With --assert, generate assertions for synthesis parallel_case and full_case.
**** Fix generate if's with empty if/else blocks. [Mat Zeno]
**** Fix generate for cell instantiations with same name. [Mat Zeno]
* Verilator 3.481 10/12/2005 Stable
*** Add /*verilator tracing_on/off*/ for waveform control.
**** Fix split optimization reordering $display statements.
* Verilator 3.480 9/27/2005 Beta
** Allow coverage of flattened modules, and multiple points per line.
Coverage analysis requires SystemPerl 1.230 or newer.
**** Add preprocessor changes to support meta-comments.
**** Optimize sequential assignments of different bits of same bus; ~5% faster.
**** Optimize away duplicate lookup tables.
**** Optimize wide concatenates into individual words. [Ralf Karge]
**** Optimize local variables from delayed array assignments.
* Verilator 3.470 9/6/2005 Stable
*** Optimize staging flops under reset blocks.
*** Add '-Werror-...' to upgrade specific warnings to errors.
**** Add GCC branch prediction hints on generated if statements.
**** Fix bad simulation when same function called twice in same expression.
**** Fix preprocessor substitution of quoted parameterized defines.
* Verilator 3.464 8/24/2005 Stable
*** Add `systemc_imp_header, for use when using --output-split.
*** Add --stats option to dump design statistics.
**** Fix core dump with clock inversion optimizations.
* Verilator 3.463 8/5/2005 Stable
*** Fixed case defaults when not last statement in case list. [Wim Michiels]
* Verilator 3.462 8/3/2005 Stable
*** Fix reordering of delayed assignments to same memory index. [Wim Michiels]
**** Fix compile error with Flex 2.5.1. [Jens Arm]
**** Fix multiply-instantiated public tasks generating non-compilable code.
* Verilator 3.461 7/28/2005 Beta
**** Fix compile error with older versions of bison. [Jeff Dutton]
* Verilator 3.460 7/27/2005 Beta
** Add -output-split option to enable faster parallel GCC compiles.
To support --output-split, the makefiles now split VM_CLASSES
into VM_CLASSES_FAST and VM_CLASSES_SLOW. This may require a
change to local makefiles.
** Support -v argument to read library files.
*** When issuing unoptimizable warning, show an example path.
**** Fix false warning when a clock is constant.
**** Fix X/Z in decimal numbers. [Wim Michiels]
**** Fix genvar statements in non-named generate blocks.
**** Fix core dump when missing newline in `define. [David van der bokke]
**** Internal tree dumps now indicate edit number that changed the node.
* Verilator 3.450 7/12/2005
** $finish will no longer exit, but set Verilated::gotFinish().
This enables support for final statements, and for other cleanup code.
If this is undesired, redefine the vl_user_finish routine. Top level
loops should use Verilated::gotFinish() as a exit condition for their
loop, and then call top->final(). To prevent a infinite loop, a
double $finish will still exit; this may be removed in future
releases.
*** Add support for SystemVerilog keywords $bits, $countones, $isunknown,
$onehot, $onehot0, always_comb, always_ff, always_latch, finish.
**** Fix "=== 1'bx" to always be false, instead of random.
* Verilator 3.440 6/28/2005 Stable
** Add Verilog 2001 generate for/if/case statements.
* Verilator 3.431 6/24/2005 Stable
*** Fix selection bugs introduced in 3.430 beta.
* Verilator 3.430 6/22/2005 Beta
** Add Verilog 2001 variable part selects [n+:m] and [n-:m]. [Wim Michiels]
* Verilator 3.422 6/10/2005 Stable
*** Added Verilog 2001 power (**) operator. [Danny Ding]
**** Fixed crash and added error message when assigning to inputs. [Ralf Karge]
**** Fixed tracing of modules with public functions.
* Verilator 3.421 6/2/2005 Beta
**** Fixed error about reserved word on non-public signals.
**** Fixed missing initialization compile errors in 3.420 beta. [Ralf Karge]
* Verilator 3.420 6/2/2005 Beta
*** Fixed case defaults when not last statement in case list. [Ralf Karge]
**** Added error message when multiple defaults in case statement.
**** Fixed crash when wire self-assigns x=x.
** Performance improvements worth ~20%
** Added -x-assign options; ~5% faster if use -x-assign=0.
**** Optimize shifts out of conditionals and if statements.
**** Optimize local 'short' wires.
**** Fixed gate optimization with top-flattened modules. [Mahesh Kumashikar]
* Verilator 3.411 5/30/2005 Stable
**** Fixed compile error in GCC 2.96. [Jeff Dutton]
* Verilator 3.410 5/25/2005 Beta
** Allow functions and tasks to be declared public.
They will become public C++ functions, with appropriate C++ types.
This allows users to make public accessor functions/tasks, instead
of having to use public variables and `systemc_header hacks.
*** Skip producing output files if all inputs are identical
This uses timestamps, similar to make. Disable with --no-skip-identical.
**** Improved compile performance with large case statements.
**** Fixed internal error in V3Table. [Jeff Dutton]
**** Fixed compile error in GCC 2.96, and with SystemC 1.2. [Jeff Dutton]
* Verilator 3.400 4/29/2005 Beta
** Internal changes to support future clocking features.
** Verilog-Perl and SystemPerl are no longer required for C++ or SystemC
output. If you want tracing or coverage analysis, they are still needed.
*** Added --sc to create pure SystemC output not requiring SystemPerl.
*** Added --pins64 to create 64 bit SystemC outputs instead of sc_bv<64>.
*** The --exe flag is now required to produce executables inside the makefile.
This was previously the case any time .cpp files were passed on the
command line.
*** Added -O3 and --inline-mult for performance tuning. [Ralf Karge]
One experiment regained 5% performance, at a cost of 300% in compile time.
*** Improved performance of large case/always statements with low fanin
by converting to internal lookup tables (ROMs).
*** Initialize SystemC port names. [S Shuba]
**** Added Doxygen comments to Verilated includes.
**** Fixed -cc pins 8 bits wide and less to be uint8_t instead of uint16_t.
**** Fixed crash when Mdir has same name as .v file. [Gernot Koch]
**** Fixed crash with size mismatches on case items. [Gernot Koch]
* Verilator 3.340 2/18/2005 Stable
*** Report misconnected pins across all modules, instead of just first error.
**** Fixed over-active inlining, resulting in compile slowness.
**** Improved large netlist compile times.
**** Added additional internal assertions.
* Verilator 3.332 1/27/2005
*** Added -E preprocess only flag, similar to GCC.
*** Added CMPCONSTLR when comparison is constant due to > or < with all ones.
**** Fixed loss of first -f file argument, introduced in 3.331.
* Verilator 3.331 1/18/2005
** The Verilog::Perl preprocessor is now C++ code inside of Verilator.
This improves performance, makes compilation easier, and enables
some future features.
*** Support arrays of instantiations (non-primitives only). [Wim Michiels]
**** Fixed unlinked error with defparam. [Shawn Wang]
* Verilator 3.320 12/10/2004
** NEWS is now renamed Changes, to support CPAN indexing.
*** If Verilator is passed a C file, create a makefile link rule.
This saves several user steps when compiling small projects.
*** Added new COMBDLY warning in place of fatal error. [Shawn Wang]
*** Fixed mis-simulation with wide-arrays under bit selects. [Ralf Karge]
**** Added NC Verilog as alternative to VCS for reference tests.
**** Support implicit wire declarations on input-only signals.
(Dangerous, as leads to wires without drivers, but allowed by spec.)
**** Fixed compile warnings on Suse 9.1
* Verilator 3.311 11/29/2004
** Support implicit wire declarations (as a warning). [Shawn Wang]
**** Fixed over-shift difference in Verilog vs C++. [Ralf Karge]
* Verilator 3.310 11/15/2004
** Support defparam.
** Support gate primitives: buf, not, and, nand, or, nor, xor, xnor.
*** Ignore all specify blocks.
* Verilator 3.302 11/12/2004
*** Support NAND and NOR operators.
*** Better warnings when port widths don't match.
**** Fixed internal error due to some port width mismatches. [Ralf Karge]
**** Fixed WIDTH warnings on modules that are only used
parameterized, not in 'default' state.
**** Fixed selection of SystemC library on cygwin systems. [Shawn Wang]
**** Fixed runtime bit-selection of parameter constants.
* Verilator 3.301 11/04/2004
**** Fixed 64 bit [31:0] = {#{}} mis-simulation. [Ralf Karge]
**** Fixed shifts greater then word width mis-simulation. [Ralf Karge]
**** Work around GCC 2.96 negation bug.
* Verilator 3.300 10/21/2004
** New backend that eliminates most VL_ macros.
Improves performance 20%-50%, depending on frequency of use of signals
over 64 bits. GCC compile times with -O2 shrink by a factor of 10.
**** Fixed "setting unsigned int from signed value" warning.
* Verilator 3.271 10/21/2004
**** Fixed "loops detected" error with some negedge clocks.
**** Cleaned up some output code spacing issues.
* Verilator 3.270 10/15/2004
*** Support Verilog 2001 parameters in module headers. [Ralf Karge]
**** Suppress numeric fault when dividing by zero.
**** Faster code to support compilers not inlining all Verilated functions.
* Verilator 3.260 10/7/2004
** Support Verilog 2001 named parameter instantiation. [Ralf Karge]
**** Return 1's when one bit wide extract indexes outside array bounds.
**** Fixed compile warnings on 64-bit operating systems.
**** Fixed incorrect dependency in .d file when setting VERILATOR_BIN.
* Verilator 3.251 9/9/2004
**** Fixed parenthesis overflow in Microsoft Visual C++ [Renga Sundararajan]
* Verilator 3.250 8/30/2004
** Support Microsoft Visual C++ [Renga Sundararajan]
*** SystemPerl 1.161+ is required.
* Verilator 3.241 8/17/2004
** Support ,'s to separate multiple assignments. [Paul Nitza]
**** Fixed shift sign extension problem using non-GCC compilers.
* Verilator 3.240 8/13/2004
** Verilator now uses 64 bit math where appropriate.
Inputs and outputs of 33-64 bits wide to the C++ Verilated model must
now be uint64_t's; SystemC has not changed, they will remain sc_bv's.
This increases performance by ~ 9% on x86 machines, varying with how
frequently 33-64 bit signals occur. Signals 9-16 bits wide are now
stored as 16 bit shorts instead of longs, this aids cache packing.
**** Fixed SystemC compile error with feedthrus. [Paul Nitza]
**** Fixed concat value error introduced in 3.230.
* Verilator 3.230 8/10/2004
*** Added coverage output to test_sp example, SystemPerl 1.160+ is required.
**** Fixed time 0 value of signals. [Hans Van Antwerpen]
Earlier versions would not evaluate some combinatorial signals
until posedge/negedge blocks had been activated.
**** Fixed wide constant inputs to public submodules [Hans Van Antwerpen]
**** Fixed wide signal width extension bug.
Only applies when width mismatch warnings were overridden.
* Verilator 3.220 6/22/2004
** Many waveform tracing changes:
*** Tracing is now supported on C++ standalone simulations. [John Brownlee]
*** When tracing, SystemPerl 1.150 or newer is required.
*** When tracing, Verilator must be called with the --trace switch.
**** Added SystemPerl example to documentation. [John Brownlee]
**** Various Cygwin compilation fixes. [John Brownlee]
* Verilator 3.210 4/1/2004
** Compiler optimization switches have changed
See the BENCHMARKING section of the documentation.
*** With Verilog-Perl 2.3 or newer, Verilator supports SystemVerilog
preprocessor extensions.
*** Added localparam. [Thomas Hawkins]
*** Added warnings for SystemVerilog reserved words.
* Verilator 3.203 3/10/2004
*** Notes and repairs for Solaris. [Fred Ma]
* Verilator 3.202 1/27/2004
** The beta version is now the primary release. See below for many changes.
If you have many problems, you may wish to try release 3.125.
*** Verilated::traceEverOn(true) must be called at time 0 if you will ever
turn on tracing (waveform dumping) of signals. Future versions will
need this switch to disable trace incompatible optimizations.
**** Fixed several tracing bugs
**** Added optimizations for common replication operations.
* Verilator 3.201-beta 12/10/2003
** BETA VERSION, USE 3.124 for stable release!
** Version 3.2XX includes a all new back-end.
This includes automatic inlining, flattening of signals between
hierarchy, and complete ordering of statements. This results in
60-300% execution speedups, though less pretty C++ output. Even
better results are possible using GCC 3.2.2 (part of Redhat 9.1), as
GCC has fixed some optimization problems which Verilator exposes.
If you are using `systemc_ctor, beware pointers to submodules are now
initialized after the constructor is called for a module, to avoid
segfaults, move statements that reference subcells into initial
statements.
*** C++ Constructor that creates a verilog module may take a char* name.
This name will be used to prefix any $display %m arguments, so users may
distinguish between multiple Verilated modules in a single executable.
* Verilator 3.125 1/27/2004
**** Optimization of bit replications
* Verilator 3.124 12/05/2003
*** A optimized executable will be made by default, in addition to a debug
executable. Invoking Verilator with --debug will pick the debug version.
**** Many minor invisible changes to support the next version.
* Verilator 3.123 11/10/2003
**** Wide bus performance enhancements.
**** Fixed function call bug when width warning suppressed. [Leon Wildman]
**** Fixed __DOT__ compile problem with funcs in last revision. [Leon Wildman]
* Verilator 3.122 10/29/2003
*** Modules which are accessed from external code now must be marked with
/*verilator public_module*/ unless they already contain public signals.
To enforce this, private cell names now have a string prepended.
**** Fixed replicated function calls in one statement. [Robert A. Clark]
**** Fixed function call bug when width warning suppressed. [Leon Wildman]
* Verilator 3.121 09/29/2003
*** Support multiplication over 32 bits. [Chris Boumenot]
Also improved speed of addition and subtraction over 32 bits.
*** Detect bit selection out of range errors.
*** Detect integer width errors.
**** Fixed width problems on function arguments. [Robert A. Clark]
* Verilator 3.120 09/24/2003
*** $finish now exits the model (via vl_finish function).
*** Support inputs/outputs in tasks.
*** Support V2K "integer int = {INITIAL_VALUE};"
*** Ignore floating point delay values. [Robert A. Clark]
**** Ignore `celldefine, `endcelldefine, etc. [Robert A. Clark]
**** New optimizations on reduction operators.
**** Fixed converting "\ooo" into octal values.
**** Fixed $display("%x");
* Verilator 3.112 09/16/2003
**** Fixed functions in continuous assignments. [Robert A. Clark]
**** Fixed inlining of modules with 2-level deep outputs.
* Verilator 3.111 09/15/2003
**** Fixed declaration of functions before using that module. [Robert A. Clark]
**** Fixed module inlining bug with outputs.
* Verilator 3.110 09/12/2003
** Support Verilog 2001 style input/output declarations. [Robert A. Clark]
*** Allow local vars in headers of function/tasks. [Leon Wildman]
* Verilator 3.109 08/28/2003
** Added support for local variables in named begin blocks. [Leon Wildman]
* Verilator 3.108 08/11/2003
** Added support for functions.
*** Signals 8 bits and shorter are now stored as chars
instead of uint32_t's. This improves Dcache packing and
improves performance by ~7%.
**** $display now usually results in a single VL_PRINT rather then many.
**** Many optimizations involving conditionals (?:)
* Verilator 3.107 07/15/2003
*** --private and --l2name are now the default,
as this enables additional optimizations.
Use --noprivate or --nol2name to get the older behavior.
*** Now support $display of binary and wide format data.
*** Added detection of incomplete case statements,
and added related optimizations worth ~4%.
**** Work around flex bug in Redhat 8.0. [Eugene Weber]
**** Added some additional C++ reserved words.
**** Additional constant optimizations, ~5% speed improvement.
* Verilator 3.106 06/17/2003
** $c can now take multiple expressions as arguments.
For example $c("foo","bar(",32+1,");") will insert "foobar(33);"
This makes it easier to pass the values of signals.
** Several changes to support future versions that may have
signal-eliminating optimizations. Users should try to use these switch
on designs, they will become the default in later versions.
*** Added --private switch and /*verilator public*/ metacomment.
This renames all signals so that compile errors will result if any
signals referenced by C++ code are missing a /*verilator public*/
metacomment.
*** With --l2name, the second level cell C++ cell is now named "v".
Previously it was named based on the name of the verilog code. This
means to get to signals, scope to "{topcell} ->v ->{mysignal}" instead
of "{topcell} ->{verilogmod}. {mysignal}". This allows different
modules to be substituted for the cell without requiring source
changes.
**** Several cleanups for Redhat 8.0.
* Verilator 3.105 05/08/2003
**** Fixed more GCC 3.2 errors. [David Black]
* Verilator 3.104 04/30/2003
*** Indicate direction of ports with VL_IN and VL_OUT.
*** Allow $c32, etc, to specify width of the $c statement for VCS.
**** Fixed false "indent underflow" error inside `systemc_ctor sections.
**** Fixed missing ordering optimizations when outputs also used internally.
*** Numerous performance improvements, worth about 25%
**** Assign constant cell pins in initial blocks rather then every cycle.
**** Promote subcell's combo logic to sequential evaluation when possible.
**** Fixed GCC 3.2 compile errors. [Narayan Bhagavatula]
* Verilator 3.103 01/28/2003
**** Fixed missing model evaluation when clock generated several levels of
hierarchy across from where it is used as a clock. [Richard Myers]
**** Fixed sign-extension bug introduced in 3.102.
* Verilator 3.102 01/24/2003
**** Fixed sign-extension of X/Z's ("32'hx")
* Verilator 3.101 01/13/2003
**** Fixed 'parameter FOO=#'bXXXX' [Richard Myers]
**** Allow spaces inside numbers ("32'h 1234") [Sam Gladstone]
* Verilator 3.100 12/23/2002
** Support for simple tasks w/o vars or I/O. [Richard Myers]
**** Ignore DOS carriage returns in Linux files. [Richard Myers]
* Verilator 3.012 12/18/2002
**** Fixed parsing bug with casex statements containing case items
with bit extracts of parameters. [Richard Myers]
**** Fixed bug which could cause writes of non-power-of-2 sized arrays to
corrupt memory beyond the size of the array. [Dan Lussier]
**** Fixed bug which did not detect UNOPT problems caused by
submodules. See the description in the verilator man page. [John Deroo]
**** Fixed compile with threaded Perl. [Ami Keren]
* Verilator 3.010 11/3/2002
*** Support SystemC 2.0.1. SystemPerl version 1.130 or newer is required.
**** Fixed bug with inlined modules under other inlined modules. [Scott
Bleiweiss]
* Verilator 3.005 10/21/2002
**** Fixed X's in case (not casex/z) to constant propagate correctly.
**** Fixed missing include. [Kurachi]
* Verilator 3.004 10/10/2002
*** Added /* verilator module_inline */ and associated optimizations.
*** Allow /* verilator coverage_block_off */ in place of `coverage_block_off.
This prevents problems with Emacs AUTORESET. [Ray Strouble]
**** Fixed `coverage_block_off also disabling subsequent blocks.
**** Fixed unrolling of loops with multiple simple statements.
**** Fixed compile warnings on newer GCC. [Kurachi]
**** Additional concatenation optimizations.
* Verilator 3.003 09/13/2002
*** Now compiles on Windows 2000 with Cygwin.
**** Fixed bug with pin assignments to wide memories.
**** Optimize wire assignments to constants.
* Verilator 3.002 08/19/2002
** First public release of version 3.
* Verilator 3.000 08/03/2002
** All new code base. Many changes too numerous to mention.
*** Approximately 4 times faster then Verilator 2.
*** Supports initial statements
*** Supports correct blocking/nonblocking assignments
*** Supports `defines across multiple modules
*** Optimizes call ordering, constant propagation, and dead code elimination.
* Verilator 2.1.8 04/03/2002
** All applications must now link against include/verilated.cpp
*** Paths specified to verilator_make should be absolute, or be formed
to allow for execution in the object directory (prepend ../ to each path.)
This allows relative filenames for makes which hash and cache dependencies.
**** Added warning when parameter constants are too large. [John Deroo]
**** Added warning when x/?'s used in non-casez statements.
**** Added warning when blocking assignments used in posedge blocks. [Dan Lussier]
**** Split evaluation function into clocked and non-clocked, 20% perf gain.
* Verilator 2.1.5 12/1/2001
** Added coverage analysis. In conjunction with SystemC provide line
coverage reports, without SystemC, provide a hook to user written
accumulation function. See --coverage option of verilator_make.
*** Relaxed multiply range checking
*** Support for constants up to 128 bits
*** Randomize values used when assigning to X's.
**** Added -guard option of internal testing.
**** Changed indentation in emitted code to be automatically generated.
**** Fixed corruption of assignments of signal over 32 bits with non-0 lsb.
* Verilator 2.1.4 11/16/2001
** Added $c("c_commands();"); for embedding arbitrary C code in Verilog.
* Verilator 2.1.3 11/03/2001
** Support for parameters.
* Verilator 2.1.2 10/25/2001
** Verilog Errors now reference the .v file rather then the .vpp file.
*** Support strings in assignments: reg [31:0] foo = "STRG";
*** Support %m in format strings. Ripped out old $info support, use
Verilog-Perl's vpm program instead.
*** Convert $stop to call of v_stop() which user can define.
**** Fixed bug where a==b==c would have wrong precedence rule.
**** Fixed bug where XNOR on odd-bit-widths (~^ or ^~) had bad value.
* Verilator 2.1.1 5/17/2001
** New test_sp directory for System-Perl (SystemC) top level instantiation
of the Verilated code, lower modules are still C++ code. (Experimental).
** New test_spp directory for Pure System-Perl (SystemC) where every module
is true SystemC code. (Experimental)
*** Input ports are now loaded by pointer reference into the sub-cell.
This is faster on I-386 machines, as the stack must be used when there are
a large number of parameters. Also, this simplifies debugging as the value
of input ports exists for tracing.
**** Many code cleanups towards standard C++ style conventions.
* Verilator 2.1.0 5/8/2001
**** Many code cleanups towards standard C++ style conventions.
* {Version history lost}
* Verilator 1.8 7/8/1996
** [Versions 0 to 1.8 were by Paul Wasson]
**** Fix single bit in concat from instance output incorrect offset bug.
* Verilator 1.7 5/20/1996
**** Mask unused bits of DONTCAREs.
* Verilator 1.6 5/13/1996
*** Added fasttrace script
* Verilator 1.5 1/9/1996
*** Pass structure pointer into translated code,
so multiple instances can use same functions.
**** Fix static value concat on casex items.
* Verilator 1.1 3/30/1995
*** Bug fixes, added verimake_partial script, performance improvements.
* Verilator 1.0c 9/30/1994
*** Initial release of Verilator
* Verilator 0.0 7/8/1994
**** First code written.
----------------------------------------------------------------------
$Id$
----------------------------------------------------------------------
This uses outline mode in Emacs. See C-h m [M-x describe-mode].
Copyright 2001-2006 by Wilson Snyder. This program is free software;
you can redistribute it and/or modify it under the terms of either the GNU
General Public License or the Perl Artistic License.
Local variables:
mode: outline
paragraph-separate: "[ \f\n]*$"
end:

353
Makefile.in Normal file
View File

@ -0,0 +1,353 @@
# $Id$
#*****************************************************************************
# DESCRIPTION: Verilator top level: Makefile pre-configure version
#
# This file is part of Verilator.
#
# Author: Wilson Snyder <wsnyder@wsnyder.org> or <wsnyder@world.std.com>
#
# Code available from: http://www.veripool.com/verilator
#
#*****************************************************************************
#
# Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# General Public License or the Perl Artistic License.
#
# Verilator is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#****************************************************************************/
#
# make all to compile and build Verilator.
# make install to install it.
# make TAGS to update tags tables.
#
# make clean or make mostlyclean
# Delete all files from the current directory that are normally
# created by building the program. Don't delete the files that
# record the configuration. Also preserve files that could be made
# by building, but normally aren't because the distribution comes
# with them.
#
# make distclean
# Delete all files from the current directory that are created by
# configuring or building the program. If you have unpacked the
# source and built the program without creating any other files,
# `make distclean' should leave only the files that were in the
# distribution.
#
# make maintainer-clean
# Delete everything from the current directory that can be
# reconstructed with this Makefile. This typically includes
# everything deleted by distclean, plus more: C source files
# produced by Bison, tags tables, info files, and so on.
#
# make extraclean
# Still more severe - delete backup and autosave files, too.
#### Start of system configuration section. ####
srcdir = @srcdir@
VPATH = @srcdir@
HOST = @HOST@
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
MAKEINFO = makeinfo
TEXI2DVI = texi2dvi
PERL = @PERL@
#### Don't edit: You're much better using configure switches to set these
prefix = @prefix@
exec_prefix = @exec_prefix@
# Directory in which to install scripts.
bindir = @bindir@
# Directory in which to install scripts.
mandir = @mandir@
# Directory in which to install library files.
datadir = @datadir@
# Directory in which to install documentation info files.
infodir = @infodir@
# Directory in which to install package specific files
# Note this gets coded into v3c/config.h also
pkgdatadir = @pkgdatadir@
#### End of system configuration section. ####
######################################################################
SHELL = /bin/sh
SUBDIRS = src test_verilated test_c test_sc test_sp test_regress test_vcs
INFOS = README verilator.txt verilator.html verilator.1 verilator.pdf
# Files that can be generated, but should be up to date for a distribution.
DISTDEP = $(INFOS) Makefile
# Files to distribute.
DISTBIN = $(wildcard bin/verilator-*)
DISTFILES_INC = $(INFOS) .cvsignore COPYING *.in \
Changes README TODO \
bin/* \
install-sh configure mkinstalldirs *.texi \
include/verilated.[chv]* \
include/verilatedos.[chv]* \
include/*.in \
src/.cvsignore src/*.in src/*.cpp src/*.[chly] src/astgen src/flexfix \
src/*.pl \
test_*/.cvsignore test_*/Makefile* test_*/*.cpp \
test_*/*.pl test_*/*.v test_*/*.vc test_*/vlint \
test_verilated/vgen*.pl \
test_regress/t/*.v* \
test_regress/t/*.pl \
test_regress/t/*.out \
verilator.* \
INST_PROJ_FILES = \
bin/verilator \
bin/verilator_includer \
include/verilated.[chv]* \
include/verilated.mk \
include/verilatedos.[chv]* \
INST_PROJ_BIN_FILES = \
verilator_bin \
verilator_bin_dbg \
DISTFILES := $(DISTFILES_INC)
all: all_nomsg msg_test
all_nomsg: verilator_exe info
.PHONY:verilator_exe
verilator_exe verilator_bin verilator_bin_dbg:
@echo ------------------------------------------------------------
@echo "making verilator in src" ; \
(cd src && $(MAKE) )
.PHONY:msg_test
msg_test:
@echo "Build complete!"
@echo
@echo "Type 'make test' to test."
@echo
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
test: test_vcs test_c test_sc test_sp test_verilated test_regress
else
test: test_c test_sc test_sp
endif
@echo "Tests passed!"
@echo
@echo "Type 'make install' to install documentation."
@echo
test_vcs: all_nomsg
@(cd test_vcs && $(MAKE))
test_c: all_nomsg
@(cd test_c && $(MAKE))
test_c_debug: all_nomsg
@(cd test_c && $(MAKE) debug)
test_sc: all_nomsg
@(cd test_sc && $(MAKE))
test_sc_debug: all_nomsg
@(cd test_sc && $(MAKE) debug)
test_sp: all_nomsg
@(cd test_sp && $(MAKE))
test_sp_debug: all_nomsg
@(cd test_sp && $(MAKE) debug)
test_verilated: all_nomsg
@(cd test_verilated && $(MAKE))
test_regress: all_nomsg
@(cd test_regress && $(MAKE))
info: $(INFOS)
# Use --no-split to avoid creating filenames > 14 chars.
verilator.1: bin/verilator
pod2man $< $@
verilator.txt: bin/verilator
pod2text $< $@
verilator.html: bin/verilator
pod2html $< >$@
verilator.pdf: bin/verilator $(DISTCONFIG)
pod2latex --full --out verilator.tex bin/verilator
cat < verilator.tex \
| sed 's/\\begin{document}/\\usepackage{fancyhdr} \\pagestyle{fancy}\n\\begin{document}/' \
| sed 's/\\begin{document}/\\setlength{\\parindent}{0pt} \\setlength{\\parskip}{\\baselineskip}\n\\begin{document}/' \
| sed 's/\\begin{document}/\\title{$(DISTTITLE)} \\date{${DISTDATE}} \\author{Wilson Snyder\\\\ http:\/\/www.veripool.com}\n\\begin{document}/' \
| sed 's/\\begin{document}/\\lhead[$(DISTTITLE)]{$(DISTTITLE)}\n\\begin{document}/' \
| sed 's/\\tableofcontents/\\begin{titlepage} \\maketitle \\end{titlepage}\n\\tableofcontents/' \
> verilator2.tex
mv verilator2.tex verilator.tex
pdflatex verilator.tex
pdflatex verilator.tex
-rm -f verilator.toc verilator.aux verilator.idx
INSTALL: install.texi
$(MAKEINFO) -I$(srcdir) $(srcdir)/install.texi --output=$@ \
--no-headers --no-validate
README: readme.texi
$(MAKEINFO) -I$(srcdir) $(srcdir)/readme.texi --output=$@ \
--no-headers --no-validate
installdirs:
$(SHELL) ${srcdir}/mkinstalldirs $(bindir) $(infodir)
# See uninstall also
VL_INST_BIN_FILES = verilator
installbin:
( cd bin ; $(INSTALL_PROGRAM) verilator $(bindir)/verilator )
VL_INST_MAN_FILES = verilator.1
installman:
for p in $(VL_INST_MAN_FILES) ; do \
$(INSTALL_PROGRAM) $$p $(mandir)/man1/$$p; \
done
install: all_nomsg info installdirs installbin installman install-msg
install-here: installman ftp
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
DIRPROJECT := $(shell project_dir --project)
INST_PROJ_CVS = cp_if_cvs_diff
install-project: dist
@echo "Install-project to $(DIRPROJECT)"
strip verilator_bin*
$(MAKE) install-project-quick
for p in verilator.1 ; do \
$(INSTALL_PROGRAM) -m 0666 $$p $(DIRPROJECT_PREFIX)/man/man1/$$p; \
done
$(INST_PROJ_CVS) $(DISTNAME).tar.gz $(DIRPROJECT)/hw/utils/verilator/verilator.tgz
rm $(DISTNAME).tar.gz
install-project-quick:
@echo "Install-project-quick (no strip) to $(DIRPROJECT)"
for p in $(INST_PROJ_FILES) ; do \
$(INST_PROJ_CVS) $$p $(DIRPROJECT)/hw/utils/verilator/$$p; \
done
for p in $(INST_PROJ_BIN_FILES) ; do \
$(INST_PROJ_CVS) $$p $(DIRPROJECT)/hw/utils/verilator/$$p-$(DIRPROJECT_ARCH); \
done
endif
ftp: info
echo "http://www.veripool.com/verilator3.html"
cp verilator.html verilator_man.html
ftp_tree --password $(VERIPOOL_PW) --user $(VERIPOOL_USER) verilator_man.html webftp.veripool.com:/web
install-msg:
@echo "Installed!"
@echo
@echo "Add to your startup file (for bash or csh, as appropriate):"
@echo " export VERILATOR_ROOT="`pwd`
@echo " setenv VERILATOR_ROOT "`pwd` " ; export VERILATOR_ROOT "
@echo
@echo "See 'verilator.txt' for documentation."
@echo
uninstall:
-cd $(mandir)/man1 && rm -f $(VL_INST_MAN_FILES)
-cd $(bindir) && rm -f $(VL_INST_BIN_FILES)
# autoheader might not change config.h.in, so touch a stamp file.
IN_WILD := ${srcdir}/*.in ${srcdir}/*/*.in ${srcdir}/*/*/*.in \
*.in */*.in */*.in
${srcdir}/config.h.in: stamp-h.in
${srcdir}/stamp-h.in: configure.in $(wildcard $(IN_WILD))
cd ${srcdir} && autoheader
echo timestamp > ${srcdir}/stamp-h.in
config.h: stamp-h
stamp-h: config.h.in config.status
./config.status
Makefile: Makefile.in config.status
./config.status
src/Makefile: src/Makefile.in config.status
./config.status
config.status: configure
./config.status --recheck
configure: configure.in
autoconf
maintainer-clean::
@echo "This command is intended for maintainers to use;"
@echo "rebuilding the deleted files requires makeinfo."
rm -f *.info* $(INFOS) faq.html verilator.html configure bin/*
clean mostlyclean distclean maintainer-clean maintainer-copy::
for dir in $(SUBDIRS); do \
echo making $@ in $$dir ; \
(cd $$dir && $(MAKE) $@) ; \
done
clean mostlyclean distclean maintainer-clean::
rm -f $(SCRIPTS) *.tmp
rm -f *.aux *.cp *.cps *.dvi *.fn *.fns *.ky *.kys *.log
rm -f *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs *.idx
rm -f *.ev *.evs *.ov *.ovs *.cv *.cvs *.ma *.mas
rm -f *.tex
distclean maintainer-clean::
rm -f Makefile config.status config.cache config.log verilator_bin* TAGS
rm -f include/verilated.mk
TAGFILES=${srcdir}/*/*.cpp ${srcdir}/*/*.h ${srcdir}/*/[a-z]*.in \
${srcdir}/[a-z]*.in ${srcdir}/*.texi
TAGS: $(TAGFILES)
etags $(TAGFILES)
######################################################################
# Distributions
DISTCONFIG = src/config.h.in
DISTTITLE := $(shell sed -e '/DTVERSION/!d' -e 's/[^0-9]*\([0-9.a-z]*\).*/verilator-\1/' -e 's/v/V/' -e q $(DISTCONFIG))
DISTNAME := $(shell sed -e '/DTVERSION/!d' -e 's/[^0-9]*\([0-9.a-z]*\).*/verilator-\1/' -e q $(DISTCONFIG))
DISTDATEPRE := $(shell sed -e '/DTVERSION/!d' -e 's/.*\([0-3]\?[0-9].[0-3]\?[0-9].[1-2][0-9][0-9][0-9]\).*/\1/' -e q $(DISTCONFIG))
DISTTAGNAME := $(subst .,_,$(subst -,_,$(DISTNAME)))
DISTDATE := $(subst /,-,$(DISTDATEPRE))
tag:
svnorcvs tag $(DISTTAGNAME)
# Don't depend on DISTFILES because there's no rule for "standards.info*".
dist: $(DISTDEP) maintainer-copy
-rm -fr $(DISTNAME)
for file in $(DISTFILES); do \
mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \
ln $$file $(DISTNAME)/$$file \
|| { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \
done; true;
chmod -R a+r $(DISTNAME)
tar chf $(DISTNAME).tar $(DISTNAME)
gzip --force --best $(DISTNAME).tar
rm -fr $(DISTNAME)
maintainer-diff:
svnorcvs diff $(DISTTAGNAME)
preexist:
test ! -r ~/src/kits/$(DISTNAME).tar.gz
maintainer-dist: preexist dist tag
cp *.gz ~/backpack
cp *.gz ~/src/kits

167
README Normal file
View File

@ -0,0 +1,167 @@
1 Verilator
***********
This is the Verilator Package.
1.1 Copyright
=============
This package is Copyright 2003-2006 by Wilson Snyder
<wsnyder@wsnyder.org>.
You may distribute under the terms of either the GNU General Public
License or the Artistic License, as specified in the Perl README file.
This code is provided with no warranty of any kind, and is used
entirely at your own risk.
1.2 Description
===============
Verilator converts synthesizable (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://veripool.com.
The resulting files are then compiled with C++. The user writes a
little C++ wrapper file, which instantiates the top level module. This
is compiled in C++, and linked with the Verilated files.
The resulting executable will perform the actual simulation.
1.3 Obtaining Distribution
==========================
The latest version is available at `http://veripool.com/verilator.htm'
Download the latest package from that site, and decompress. `gunzip
verilator_version.tar.gz ; tar xvf verilator_version.tar'
1.4 Directory Structure
=======================
The directories after de-taring are as follows:
* bin/verilator => Compiler Wrapper invoked on user Verilog 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.v => Stub defines for linting
* include/verilated.mk => Common makefile
* src/ => Translator source code
* test_v => Example Verilog code for other test dirs
* test_c => Example Verilog->C++ conversion
* test_sc => Example Verilog->SystemC conversion
* test_sp => Example Verilog->SystemPerl conversion
* test_vcs => Example Verilog->VCS conversion (test the
test)
* test_verilated => Internal tests
* test_regress => Internal tests
1.5 Supported Systems
=====================
This version of verilator has been built and tested on:
* SuSE AMD64 i686-linux-2.6.5
Other users report success with Redhat Linux 2.4, Windows under
Cygwin, HPUX and Solaris. It should run with minor porting on any Unix
system.
1.6 Installation
================
1. If you will be using SystemC (vs straight C++ output), download
SystemC 2.0.1 from `http://www.systemc.org'. Follow their
installation instructions. As described in the System-Perl README,
you will need to set SYSTEMC and/or SYSTEMC_KIT to point to this
download. Also, set SYSTEMC_ARCH to the architecture name you used
with SystemC, generally 'linux' or 'cygwin'.
2. If you will be using SystemC, download and install Verilog-Perl,
`http://search.cpan.org/search?module=Verilog::Language'.
3. If you will be using SystemC, download and install System-Perl,
`http://search.cpan.org/search?module=SystemC::Netlist'. Note
you'll need to set a `SYSTEMPERL' environment variable to point to
the downloaded kit (not the installed files.) Also, make sure to
do a `make sc_patch'.
4. `cd' to the Verilator directory containing this README.
5. Type `./configure' to configure Verilator for your system.
6. Type `make' to compile Verilator.
On Cygwin (Windows) you may get a error about libperl.a not being
found. You need to copy your perl libraries as follows.
1. Type `perl -MExtUtils::Embed -e ldopts'
2. It will show a directory name ending in /CORE. cd to that
directory.
3. `cp libperl5_6_1.a libperl.a'
4. `cp libperl5_6_1.dll libperl.dll'
5. `cp libperl5_6_1.def libperl.def'
7. Type `make test' to check the compilation.
You may get a error about the Bit::Vector perl package. You will
need to install it if you want the tests to pass. (Try `make
test_c' for a smaller test that doesn't require it.)
You may get a error about a typedef conflict for uint32_t. Edit
verilated.h to change the typedef to work, probably to `typedef
unsigned long uint32_t;'.
If you get warnings, you might want to edit `include/verilated.mk'
to delete the lines that define VK_CPPFLAGS_WALL.
8. There is no installation at present; this package runs from the
distribution directory. Programs should set the environment
variable VERILATOR_ROOT to point to this distribution, then execute
$VERILATOR_ROOT/bin/verilator, which will find the path to all
needed files.
Verilator assumes you did a make in the SystemC kit directory. If
not, you will need to populate `$SYSTEMC/include' and
`$SYSTEMC/lib-linux' appropriately.
If you will be modifying Verilator, you will probably want a second
stable copy of this kit for others to use while you experiment.
9. Detailed documentation and the man page can be seen by running:
bin/verilator -help
or reading verilator.txt in the same directory as this README.
1.7 Limitations
===============
See verilator.txt (or execute `bin/verilator --help') for limitations.

135
TODO Normal file
View File

@ -0,0 +1,135 @@
// $Id$
// DESCRIPTION: Verilator: List of To Do issues.
//
// Copyright 2004-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
Features:
Finish 3.400 new ordering fixes
Latch optimizations {Need here}
Task I/Os connecting to non-simple variables.
Fix nested casez statements expanding into to huge C++. [JeanPaul Vanitegem]
Fix FileLine memory inefficiency
Fix ordering of each bit separately in a signal (mips)
assign b[3:0] = b[7:4]; assign b[7:4] = in;
Support gate primitives/ cell libraries from xilinx, etc
Assign dont_care value to an 1'bzzz assignment
Function to eval combo logic after /*verilator public*/ functions [gwaters]
Support generated clocks (correctness)
?gcov coverage
Selectable SystemC types based on widths (see notes below)
Compile time
Inline first level trace routines
Expression coverage (see notes)
More Verilog 2001 Support
C-style function and task arguments. [Wim Michiels]
(* *) Attributes (just ignore -- preprocessor?)
Real numbers (NEVER)
Recursive functions (NEVER)
Verilog configuration files (NEVER)
Long-term Features
Assertions
VHDL parser [Philips]
Tristate support
SystemPerl integration
Multithreaded execution
Front end parser integration into Verilog-Perl like Netlist
Testing:
Capture all inputs into global "rerun it" file
Code to make wrapper that sets signals, so can do comparison checks
New random program generator
Better graph viewer with search and zoom
Port and test against opencores.org code
Performance:
Constant propagation
Extra cleaning AND: 1 & ((VARREF >> 1) | ((&VARREF >> 1) & VARREF))
Extra shift (perhaps due to clean): if (1 & CAST (VARREF >> #))
Gated clock and latch conversion to flops. [JeanPaul Vanitegem]
Could propagate the AND into pos/negedges and let domaining optimize.
Negedge reset
Switch to remove negedges that don't matter
Can't remove async resets from control flops (like in syncronizers)
If all references to array have a constant index, blow up into separate signals-per-index
Multithreaded execution
Bit-multiply for faster bit swapping and a=b[1,3,2] random bit reorderings.
Move _last sets and all other combo logic inside master
if() that triggers on all possible sense items
Rewrite and combine V3Life, V3Subst
If block temp only ever set in one place to constant, propagate it
Used in t_mem for array delayed assignments
Replace variables if set later in same cfunc branch
See for example duplicate sets of _narrow in cycle 90/91 of t_select_plusloop
Same assignment on both if branches
"if (a) { ... b=2; } else { ... b=2;}" -> "b=2; if ..."
Careful though, as b could appear in the statement or multiple times in statement
(Could just require exatly two 'b's in statement)
Simplify XOR/XNOR/AND/OR bit selection trees
Foo = A[1] ^ A[2] ^ A[3] etc are better as ^ ( A & 32'b...1110 )
Combine variables into wider elements
Parallel statements on different bits should become single signal
Variables that are always consumed in "parallel" can be joined
Duplicate assignments in gate optimization
Common to have many separate posedge blocks, each with identical
reset_r <= rst_in
*If signal is used only once (not counting trace), always gate substitute
Don't merge if any combining would form circ logic (out goes back to in)
Multiple assignments each bit can become single assign with concat
Make sure a SEL of a CONCAT can get the single bit back.
Usually blocks/values
Enable only after certain time, so VL_TIME_I(32) > 0x1e gets eliminated out
Better ordering of a<=b, b<=c, put all refs to 'b' next to each other to optimize caching
Allow Split of case statements without a $display/$stop
I-cache packing improvements (what/how?)
Data cache organization (order of vars in class)
First have clocks,
then bools instead of uint32_t's
then based on what sense list they come from, all outputs, then all inputs
finally have any signals part of a "usually" block, or constant.
Rather then tracking widths, have a MSB...LSB of this expression
(or better, a bitmask of bits relevant in this expression)
Track recirculation and convert into clock-enables
Clock enables should become new clocking domains for speed
If floped(a) & flopped(b) and no other a&b, then instead flop(a&b).
//**********************************************************************
//* Detailed notes on 'todo' features
Selectable SystemC types based on widths (see notes below)
Statements:
verilator sc_type uint32_t {override specific I/O signals}
verilator sc_typedef width 1 bool
verilator sc_typedef width 2-32 uint32_t
verilator sc_typedef width 33-64 uint64_t
verilator sc_typedef width 65+ sc_bv<width> //<< programmable width
Replace `systemc_clock, --pins64
Have accessor function with type overloading to get or set the pin value.
(Get rid of getdatap mess?)
//**********************************************************************
//* Eventual tristate bus Stuff allowed (old verilator)
1) Tristate assignments must be continuous assignments
The RHS of a tristate assignment can be the following
a) a node (tristate or non-tristate)
b) a constant (must be all or no z's)
x'b0, x'bz, x{x'bz}, x{x'b0} -> are allowed
c) a conditional whose possible values are (a) or (b)
2) One can lose that fact that a node is a tristate node. This happens
if a tristate node is assigned to a 'standard' node, or is used on
RHS of a conditional. The following infer tristate signals:
a) inout <SIGNAL>
b) tri <SIGNAL>
c) assigning to 'Z' (maybe through a conditional)
Note: tristate-ness of an output port determined only by
statements in the module (not the instances it calls)
4) Tristate variables can't be multidimensional arrays
5) Only check tristate contention between modules (not within!)
6) Only simple compares with 'Z' are allowed (===)

1622
bin/verilator Executable file

File diff suppressed because it is too large Load Diff

196
bin/verilator_difftree Executable file
View File

@ -0,0 +1,196 @@
: # -*-Mode: perl;-*- use perl, wherever it is
eval 'exec perl -wS $0 ${1+"$@"}'
if 0;
# $Id$
######################################################################
#
# Copyright 2005-2006 by Wilson Snyder <wsnyder@wsnyder.org>. This
# program is free software; you can redistribute it and/or modify it under
# the terms of either the GNU Lesser General Public License or the Perl
# Artistic License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
######################################################################
require 5.006_001;
use warnings;
use Getopt::Long;
use IO::File;
use Pod::Usage;
use strict;
use vars qw ($Debug);
#======================================================================
#======================================================================
# main
$Debug = 0;
my $Opt_A;
my $Opt_B;
autoflush STDOUT 1;
autoflush STDERR 1;
if (! GetOptions (
"help" => \&usage,
"debug" => \&debug,
"<>" => \&parameter,
)) {
usage();
}
defined $Opt_A or die "%Error: No old diff filename\n";
defined $Opt_B or die "%Error: No new diff filename\n";
if (-d $Opt_A && -d $Opt_B) {
diff_dir ($Opt_A, $Opt_B);
} elsif (-f $Opt_A && -f $Opt_B) {
diff_file ($Opt_A, $Opt_B);
} else {
die "%Error: Mix of files and dirs\n";
}
sub diff_dir {
my $a = shift;
my $b = shift;
# Diff all files under two directories
my %files;
foreach my $fn (glob("$a/*.tree")) {
(my $base = $fn) =~ s!.*/!!;
$files{$base}{a} = $fn;
}
foreach my $fn (glob("$b/*.tree")) {
(my $base = $fn) =~ s!.*/!!;
$files{$base}{b} = $fn;
}
foreach my $base (sort (keys %files)) {
my $a = $files{$base}{a};
my $b = $files{$base}{b};
next if !$a || !$b;
print "="x70,"\n";
print "= $a <-> $b\n";
diff_file($a,$b);
}
}
sub diff_file {
my $a = shift;
my $b = shift;
# Compare the two tree files
my $tmp_a = "/tmp/$$.a";
my $tmp_b = "/tmp/$$.b";
filter ($a, $tmp_a);
filter ($b, $tmp_b);
system("diff $tmp_a $tmp_b");
unlink $tmp_a;
unlink $tmp_b;
}
sub filter {
my $fn1 = shift;
my $fn2 = shift;
# Remove hex numbers before diffing
my $f1 = IO::File->new ($fn1) or die "%Error: $! $fn1,";
my $f2 = IO::File->new ($fn2,"w") or die "%Error: $! $fn2,";
while (defined (my $line=$f1->getline())) {
next if $line =~ / This=/;
$line =~ s/0x[a-f0-9]+/0x/g;
$line =~ s/<e[0-9]+\#?>/<e>/g;
print $f2 $line;
}
$f1->close;
$f2->close;
}
#----------------------------------------------------------------------
sub usage {
print '$Id$ ', "\n";
pod2usage(-verbose=>2, -exitval => 2);
exit (1);
}
sub debug {
$Debug = 1;
}
sub parameter {
my $param = shift;
if (!defined $Opt_A) {
$Opt_A = $param;
} elsif (!defined $Opt_B) {
$Opt_B = $param;
} else {
die "%Error: Unknown parameter: $param\n";
}
}
#######################################################################
sub run {
# Run a system command, check errors
my $command = shift;
print "\t$command\n";
system "$command";
my $status = $?;
($status == 0) or die "%Error: Command Failed $command, $status, stopped";
}
#######################################################################
__END__
=pod
=head1 NAME
verilator_difftree - Compare two Verilator debugging trees
=head1 SYNOPSIS
verilator_difftree .../a/a.tree .../b/a.tree
verilator_difftree .../a .../b
=head1 DESCRIPTION
Verilator_difftree is used for debugging Verilator tree output files. It
performs a diff between two files, or all files common between two
directories, ignoring irrelevant pointer differences.
=head1 ARGUMENTS
=over 4
=item --help
Displays this message and program version and exits.
=back
=head1 DISTRIBUTION
The latest version is available from L<http://www.veripool.com/>.
Copyright 2005-2006 by Wilson Snyder. This package is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License or the Perl Artistic License.
=head1 AUTHORS
Wilson Snyder <wsnyder@wsnyder.org>
=head1 SEE ALSO
C<verilator>
=cut
######################################################################
### Local Variables:
### compile-command: "$V4/bin/verilator_difftree ~/SandBox/workwsnyder/verilator/test_c/obj_dir/V*_03_*.tree $V4/test_c/obj_dir/V*_03_*.tree"
### End:

16
bin/verilator_includer Executable file
View File

@ -0,0 +1,16 @@
: # -*-Mode: perl;-*- use perl, wherever it is
eval 'exec perl -wS $0 ${1+"$@"}'
if 0;
# $Id$
# DESCRIPTION: Print include statements for each ARGV
#
# Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# General Public License or the Perl Artistic License.
######################################################################
require 5.005;
use warnings;
foreach my $param (@ARGV) {
print "#include \"$param\"\n"
}

49
configure.in Normal file
View File

@ -0,0 +1,49 @@
dnl $Id$
dnl DESCRIPTION: Process this file with autoconf to produce a configure script.
dnl Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
dnl redistribute it and/or modify it under the terms of either the GNU
dnl General Public License or the Perl Artistic License.
AC_REVISION($Revision$)dnl
AC_INIT(src/Verilator.cpp)
AC_CONFIG_HEADER(src/config.h)
dnl Special Substitutions
dnl Checks for programs.
CXX=g++
AC_PROG_CC
AC_PROG_CXX
AC_PROG_INSTALL
AC_PATH_PROG(PERL,perl)
dnl Checks for libraries.
dnl Checks for header files.
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS(fcntl.h unistd.h sys/file.h sys/time.h sys/un.h math.h stdint.h mingw/stdint.h)
dnl Checks for typedefs, structures
AC_CHECK_TYPE(size_t,unsigned int)
AC_CHECK_TYPE(uint_t,unsigned int)
AC_CHECK_TYPE(ulong_t,unsigned long)
AC_TYPE_SIZE_T
AC_STRUCT_TM
dnl Checks for compiler characteristics.
AC_C_INLINE
dnl Checks for library functions.
dnl Checks for system services
dnl Other install directories
pkgdatadir=${datadir}/verilator
AC_SUBST(pkgdatadir)
AC_OUTPUT(Makefile src/Makefile src/Makefile_obj include/verilated.mk)
AC_MSG_RESULT([])
AC_MSG_RESULT([Now type 'make'])
AC_MSG_RESULT([])

1
include/.cvsignore Normal file
View File

@ -0,0 +1 @@
verilated.mk

260
include/verilated.cpp Normal file
View File

@ -0,0 +1,260 @@
// $Id$ -*- C++ -*-
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//=========================================================================
///
/// \file
/// \brief Verilator: Linked against all applications using Verilated source.
///
/// This file must be compiled and linked against all objects
/// created from Verilator.
///
/// Code available from: http://www.veripool.com/verilator
///
//=========================================================================
#include "verilated.h"
#define VL_VALUE_STRING_MAX_WIDTH 1024 ///< Max static char array for VL_VALUE_STRING
//===========================================================================
// Global variables
uint32_t Verilated::s_coverageRequest = false;
int Verilated::s_randReset = false;
int Verilated::s_debug = 1;
bool Verilated::s_calcUnusedSigs = false;
bool Verilated::s_gotFinish = false;
bool Verilated::s_assertOn = true;
//===========================================================================
// User definable functions
#ifndef VL_USER_FINISH // Define this to override this function
void vl_finish (const char* filename, int linenum, const char* hier) {
if (0 && hier) {}
VL_PRINTF("- %s:%d: Verilog $finish\n", filename, linenum);
if (Verilated::gotFinish()) {
VL_PRINTF("- %s:%d: Second verilog $finish, exiting\n", filename, linenum);
exit(0);
}
Verilated::gotFinish(true);
}
#endif
#ifndef VL_USER_STOP // Define this to override this function
void vl_stop (const char* filename, int linenum, const char* hier) {
Verilated::gotFinish(true);
vl_fatal (filename,linenum,hier,"Verilog $stop");
}
#endif
#ifndef VL_USER_FATAL // Define this to override this function
void vl_fatal (const char* filename, int linenum, const char* hier, const char* msg) {
if (0 && hier) {}
Verilated::gotFinish(true);
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg);
abort();
}
#endif
//===========================================================================
// Random reset -- Only called at init time, so don't inline.
IData VL_RAND32() {
#ifdef _MSC_VER
return (rand()<<16) | rand();
#else
return (lrand48()<<16) | lrand48();
#endif
}
IData VL_RAND_RESET_I(int outBits) {
if (Verilated::randReset()==0) return 0;
IData data = ~0;
if (Verilated::randReset()!=1) { // if 2, randomize
data = VL_RAND32();
}
if (outBits<32) data &= VL_MASK_I(outBits);
return data;
}
QData VL_RAND_RESET_Q(int outBits) {
if (Verilated::randReset()==0) return 0;
QData data = VL_ULL(~0);
if (Verilated::randReset()!=1) { // if 2, randomize
data = ((QData)VL_RAND32()<<VL_ULL(32)) | (QData)VL_RAND32();
}
if (outBits<64) data &= VL_MASK_Q(outBits);
return data;
}
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) {
for (int i=0; i<VL_WORDS_I(obits); i++) {
if (i<(VL_WORDS_I(obits)-1)) {
outwp[i] = VL_RAND_RESET_I(32);
} else {
outwp[i] = VL_RAND_RESET_I(32) & VL_MASK_I(obits);
}
}
return outwp;
}
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) {
for (int i=0; i<VL_WORDS_I(obits); i++) outwp[i] = 0;
return outwp;
}
//===========================================================================
// Formatting
const char* VL_VALUE_FORMATTED_Q(int obits, char fmt, bool drop0, QData ld) {
// Convert value into %b/%o/%x/%s/%u/%d formatted string
// Note uses a single buffer; presumes only one call per printf
static VL_THREAD char str[VL_VALUE_STRING_MAX_WIDTH];
char* strp = &str[0];
int lsb=obits-1;
if (drop0) while (lsb && !VL_BITISSET_Q(ld,lsb)) lsb--;
switch (fmt) {
case 'd':
sprintf(str,"%lld",(vlsint64_t)(VL_EXTENDS_QQ(obits,obits,ld)));
return str;
case 'u':
sprintf(str,"%llu",ld);
return str;
case 's':
for (; lsb>=0; lsb--) {
lsb = (lsb / 8) * 8; // Next digit
IData charval = (ld>>VL_BITBIT_Q(lsb)) & 0xff;
*strp++ = (charval==0)?' ':charval;
}
*strp++ = '\0';
return str;
case 'b':
for (; lsb>=0; lsb--) {
*strp++ = ((ld>>VL_BITBIT_Q(lsb)) & 1) + '0';
}
*strp++ = '\0';
return str;
case 'o':
for (; lsb>=0; lsb--) {
lsb = (lsb / 3) * 3; // Next digit
*strp++ = ((ld>>VL_BITBIT_Q(lsb)) & 7) + '0';
}
*strp++ = '\0';
return str;
default:
for (; lsb>=0; lsb--) {
lsb = (lsb / 4) * 4; // Next digit
IData charval = (ld>>VL_BITBIT_Q(lsb)) & 0xf;
*strp++ = (charval + ((charval < 10) ? '0':('a'-10)));
}
*strp++ = '\0';
return str;
}
*strp++ = '\0';
return str;
}
const char* VL_VALUE_FORMATTED_W(int obits, char fmt, bool drop0, WDataInP lwp) {
// Convert value into %b/%o/%x/%s/%u/%d formatted string
// Note uses a single buffer; presumes only one call per printf
static VL_THREAD char str[VL_VALUE_STRING_MAX_WIDTH];
char* strp = &str[0];
int lsb=obits-1;
if (drop0) while (lsb && !VL_BITISSET_W(lwp,lsb)) lsb--;
switch (fmt) {
case 's':
for (; lsb>=0; lsb--) {
lsb = (lsb / 8) * 8; // Next digit
IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xff;
*strp++ = (charval==0)?' ':charval;
}
*strp++ = '\0';
return str;
case 'b':
for (; lsb>=0; lsb--) {
*strp++ = ((lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 1) + '0';
}
*strp++ = '\0';
return str;
case 'o':
for (; lsb>=0; lsb--) {
lsb = (lsb / 3) * 3; // Next digit
*strp++ = ((lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 7) + '0';
}
*strp++ = '\0';
return str;
default:
for (; lsb>=0; lsb--) {
lsb = (lsb / 4) * 4; // Next digit
IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xf;
*strp++ = (charval + ((charval < 10) ? '0':('a'-10)));
}
*strp++ = '\0';
return str;
}
*strp++ = '\0';
return str;
}
//===========================================================================
// File I/O
void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) {
int lsb=obits-1;
bool start=true;
char* destp = destoutp;
for (; lsb>=0; lsb--) {
lsb = (lsb / 8) * 8; // Next digit
IData charval = (sourcep[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xff;
if (!start || charval) {
*destp++ = (charval==0)?' ':charval;
start = false; // Drop leading 0s
}
}
*destp++ = '\0'; // Terminate
while (isspace(*(destp-1)) && destp>destoutp) *--destp = '\0'; // Drop trailing spaces
}
QData VL_FOPEN_QI(QData filename, IData mode) {
IData fnw[2]; VL_SET_WQ(fnw, filename);
return VL_FOPEN_WI(2, fnw, mode);
}
QData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode) {
char filenamez[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
_VL_VINT_TO_STRING(fnwords*VL_WORDSIZE, filenamez, filenamep);
char modez[5];
_VL_VINT_TO_STRING(VL_WORDSIZE, modez, &mode);
return VL_CVT_FP_Q(fopen(filenamez,modez));
}
//===========================================================================
// Verilated:: Methods
const char* Verilated::catName(const char* n1, const char* n2) {
// Returns new'ed data
// Used by symbol table creation to make module names
char* str = new char[strlen(n1)+strlen(n2)+2];
strcpy(str,n1);
strcat(str,n2);
return str;
}
//===========================================================================
// VerilatedModule:: Methods
VerilatedModule::VerilatedModule(const char* name)
: m_name(name) {
}
//===========================================================================

1341
include/verilated.h Normal file

File diff suppressed because it is too large Load Diff

168
include/verilated.mk.in Normal file
View File

@ -0,0 +1,168 @@
# $Id$ -*- Makefile -*-
######################################################################
# DESCRIPTION: Makefile commands for all verilated target files
#
# Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# General Public License or the Perl Artistic License.
######################################################################
PERL = @PERL@
CXX = @CXX@
LINK = @CXX@
AR = ar
RANLIB = ranlib
######################################################################
# Programs
SP_PREPROC = sp_preproc
SP_INCLUDER = $(PERL) $(VERILATOR_ROOT)/bin/verilator_includer
######################################################################
# C Preprocessor flags
VK_CPPFLAGS_ALWAYS += \
-MMD \
-I$(VERILATOR_ROOT)/include \
-DVL_PRINTF=printf \
-DVM_TRACE=$(VM_TRACE) \
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
VK_CPPFLAGS_WALL += -Wall \
-Wno-char-subscripts \
-Wno-sign-compare \
-Wno-unused-parameter \
-Wno-unused-variable \
-Wno-uninitialized \
-Werror
endif
CPPFLAGS += -I. $(VK_CPPFLAGS_ALWAYS) $(VK_CPPFLAGS_WALL)
VPATH += ..
VPATH += $(VERILATOR_ROOT)/include
#OPT = -ggdb -DPRINTINITSTR -DDETECTCHANGE
#OPT = -ggdb -DPRINTINITSTR
CPPFLAGS += $(OPT)
# See the benchmarking section of bin/verilator.
# Support class optimizations. This includes the tracing and symbol table.
# SystemC takes minutes to optimize, thus it is off by default.
#OPT_SLOW =
# Fast path optimizations. Most time is spent in these classes.
#OPT_FAST = -O2 -funroll-loops
#OPT_FAST = -O
#OPT_FAST =
#######################################################################
##### Aggregates
VM_CLASSES += $(VM_CLASSES_FAST) $(VM_CLASSES_SLOW)
VM_SUPPORT += $(VM_SUPPORT_FAST) $(VM_SUPPORT_SLOW)
#######################################################################
##### SystemC or SystemPerl builds
ifeq ($(VM_SP_OR_SC),1)
CPPFLAGS += $(SYSTEMC_CXX_FLAGS) -I$(SYSTEMC)/include
LDFLAGS += $(SYSTEMC_CXX_FLAGS) -L$(SYSTEMC)/lib-$(VM_SC_TARGET_ARCH)
LIBS += -lm -lstdc++
SC_LIBS = -lsystemc
ifneq ($(wildcard $(SYSTEMC)/lib-$(VM_SC_TARGET_ARCH)/*numeric_bit*),)
# Systemc 1.2.1beta
SC_LIBS += -lnumeric_bit -lqt
endif
endif
#######################################################################
##### SystemPerl builds
ifeq ($(VM_SP),1)
CPPFLAGS += -I$(SYSTEMPERL) -I$(SYSTEMPERL)/src -DSYSTEMPERL
VPATH += $(SYSTEMPERL) $(SYSTEMPERL)/src
VK_CLASSES_SP = $(addsuffix .sp, $(VM_CLASSES))
# This rule is called manually by the upper level makefile
preproc:
@echo " SP Preprocess" $(basename $(VM_CLASSES)) ...
$(SP_PREPROC) -M sp_preproc.d --tree $(VM_PREFIX).sp_tree \
--preproc $(VK_CLASSES_SP)
else
preproc:
endif
#######################################################################
##### C/H builds
ifeq ($(VM_PCLI),1)
LIBS += -lm -lstdc++
ifeq ($(VM_TRACE),1)
CPPFLAGS += -I$(SYSTEMPERL) -I$(SYSTEMPERL)/src
VPATH += $(SYSTEMPERL) $(SYSTEMPERL)/src
endif
endif
#######################################################################
# Overall Objects Linking
VK_CLASSES_H = $(addsuffix .h, $(VM_CLASSES))
VK_CLASSES_CPP = $(addsuffix .cpp, $(VM_CLASSES))
VK_SUPPORT_CPP = $(addsuffix .cpp, $(VM_SUPPORT))
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
ifneq ($(VM_PARALLEL_BUILDS),1)
# Fast building, all .cpp's in one fell swoop
# This saves about 5 sec per module, but can be slower if only a little changes
VK_OBJS += $(VM_PREFIX)__ALLcls.o $(VM_PREFIX)__ALLsup.o
all_cpp: $(VM_PREFIX)__ALLcls.cpp $(VM_PREFIX)__ALLsup.cpp
$(VM_PREFIX)__ALLcls.cpp: $(VK_CLASSES_CPP)
$(SP_INCLUDER) $^ > $@
$(VM_PREFIX)__ALLsup.cpp: $(VK_SUPPORT_CPP)
$(SP_INCLUDER) $^ > $@
else
#Slow way of building... Each .cpp file by itself
VK_OBJS += $(addsuffix .o, $(VM_CLASSES) $(VM_SUPPORT))
endif
$(VM_PREFIX)__ALL.a: $(VK_OBJS)
@echo " Archiving" $@ ...
$(AR) r $@ $^
$(RANLIB) $@
######################################################################
### Compile rules
#Default rule embedded in make: (Not defined so user makefiles can override it)
#.cpp.o:
# $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
$(VM_PREFIX)__ALLsup.o: $(VM_PREFIX)__ALLsup.cpp
$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_SLOW) -c -o $@ $<
$(VM_PREFIX)__ALLcls.o: $(VM_PREFIX)__ALLcls.cpp
$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $<
######################################################################
### Debugging
debug::
@echo
@echo VM_PREFIX: $(VM_PREFIX)
@echo VM_CLASSES_FAST: $(VM_CLASSES_FAST)
@echo VM_CLASSES_SLOW: $(VM_CLASSES_SLOW)
@echo VM_SUPPORT_FAST: $(VM_SUPPORT_FAST)
@echo VM_SUPPORT_SLOW: $(VM_SUPPORT_SLOW)
@echo
######################################################################
### Detect out of date files and rebuild.
DEPS := $(wildcard *.d)
ifneq ($(DEPS),)
include $(DEPS)
endif

36
include/verilated.v Normal file
View File

@ -0,0 +1,36 @@
// $Id$ -*- C++ -*-
//*************************************************************************
//
// Code available from: http://www.veripool.com/verilator
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// This is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//=========================================================================
//
// DESCRIPTION: Verilator: Include in verilog files to hide verilator defines
`ifdef _VERILATED_V_ `else
`define _VERILATED_V_ 1
// Hide verilator pragmas from other tools
`ifdef verilator `else
`define coverage_block_off
`endif
// Hide file descriptor difference
`ifdef verilator
`define verilator_file_descriptor reg [63:0]
`else
`define verilator_file_descriptor integer
`endif
`endif // guard

125
include/verilatedos.h Normal file
View File

@ -0,0 +1,125 @@
// $Id$ -*- C++ -*-
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
///
/// \file
/// \brief Verilator: Common include for OS portability (verilated & verilator)
///
/// This header is used by both verilated code, and the verilator
/// program itself Code needed by only one goes into verilated.h or
/// config.h.in respectively.
///
/// Code available from: http://www.veripool.com/verilator
///
//*************************************************************************
#ifndef _VERILATEDOS_H_
#define _VERILATEDOS_H_ 1 ///< Header Guard
//=========================================================================
// Compiler pragma abstraction
#ifdef __GNUC__
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__ ((format (printf, fmtArgNum, fmtArgNum+1)))
# define VL_ATTR_ALIGNED(alignment) __attribute__ ((aligned (alignment)))
# define VL_ATTR_NORETURN __attribute__ ((noreturn))
# define VL_ATTR_UNUSED __attribute__ ((unused))
# define VL_LIKELY(x) __builtin_expect(!!(x), 1)
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
# define VL_ATTR_PRINTF(fmtArgNum) ///< Function with printf format checking
# define VL_ATTR_ALIGNED(alignment) ///< Align structure to specified byte alignment
# define VL_ATTR_NORETURN ///< Function does not ever return
# define VL_ATTR_UNUSED ///< Function that may be never used
# define VL_LIKELY(x) (!!(x)) ///< Boolean expression more often true then false
# define VL_UNLIKELY(x) (!!(x)) ///< Boolean expression more often false then true
#endif
#ifdef VL_THREADED
# ifdef __GNUC__
# define VL_THREAD __thread ///< Storage class for thread-local storage
# else
# error "Unsupported compiler for VL_THREADED: No thread-local declarator"
# endif
#else
# define VL_THREAD ///< Storage class for thread-local storage
#endif
#ifdef _MSC_VER
# define VL_ULL(c) (c##ui64) ///< Add appropriate suffix to 64-bit constant
#else
# define VL_ULL(c) (c##ULL) ///< Add appropriate suffix to 64-bit constant
#endif
//=========================================================================
// Basic integer types
#ifdef VL_UINTS_DEFINED
#elif defined(__CYGWIN__)
# include <stdint.h>
typedef unsigned char uint8_t; ///< 8-bit basic type
typedef unsigned short int uint16_t; ///< 16-bit basic type
typedef unsigned long uint32_t; ///< 32-bit basic type
typedef unsigned long long vluint64_t; ///< 64-bit basic type
typedef long vlsint32_t; ///< 32-bit signed type
typedef long long vlsint64_t; ///< 64-bit signed type
#elif defined(_WIN32)
typedef unsigned char uint8_t; ///< 8-bit basic type
typedef unsigned short int uint16_t; ///< 16-bit basic type
typedef unsigned int uint32_t; ///< 32-bit basic type
typedef unsigned __int64 vluint64_t; ///< 64-bit basic type
typedef int vlsint32_t; ///< 32-bit signed type
typedef __int64 vlsint64_t; ///< 64-bit signed type
#else // Linux or compliant Unix flavors
# include <stdint.h>
typedef unsigned long long vluint64_t; ///< 64-bit basic type
typedef long vlsint32_t; ///< 32-bit signed type
typedef long long vlsint64_t; ///< 64-bit signed type
#endif
//=========================================================================
// Integer size macros
#define VL_WORDSIZE 32 ///< Bits in a word
#define VL_QUADSIZE 64 ///< Bits in a quadword
#define VL_WORDSIZE_LOG2 5 ///< log2(VL_WORDSIZE)
/// Words this number of bits needs (1 bit=1 word)
#define VL_WORDS_I(nbits) (((nbits)+(VL_WORDSIZE-1))/VL_WORDSIZE)
//=========================================================================
// Verilated function size macros
#define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation
#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
//=========================================================================
// Base macros
#define VL_SIZEBITS_I (VL_WORDSIZE-1) ///< Bit mask for bits in a word
#define VL_SIZEBITS_Q (VL_QUADSIZE-1) ///< Bit mask for bits in a quad
/// Mask for words with 1's where relevant bits are (0=all bits)
#define VL_MASK_I(nbits) (((nbits) & VL_SIZEBITS_I) \
? ((1U << ((nbits) & VL_SIZEBITS_I) )-1) : ~0)
/// Mask for quads with 1's where relevant bits are (0=all bits)
#define VL_MASK_Q(nbits) (((nbits) & VL_SIZEBITS_Q) \
? ((VL_ULL(1) << ((nbits) & VL_SIZEBITS_Q) )-VL_ULL(1)) : VL_ULL(~0))
#define VL_BITWORD_I(bit) ((bit)/VL_WORDSIZE) ///< Word number for a wide quantity
#define VL_BITBIT_I(bit) ((bit)&VL_SIZEBITS_I) ///< Bit number for a bit in a long
#define VL_BITBIT_Q(bit) ((bit)&VL_SIZEBITS_Q) ///< Bit number for a bit in a quad
//=========================================================================
#endif /*guard*/

250
install-sh Normal file
View File

@ -0,0 +1,250 @@
#! /bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
true
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
else
instcmd=mkdir
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
then
true
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
true
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
true
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
true
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
true
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

40
mkinstalldirs Normal file
View File

@ -0,0 +1,40 @@
#! /bin/sh
# mkinstalldirs --- make directory hierarchy
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# Created: 1993-05-16
# Public domain
# $Id:$
errstatus=0
for file
do
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
shift
pathcomp=
for d
do
pathcomp="$pathcomp$d"
case "$pathcomp" in
-* ) pathcomp=./$pathcomp ;;
esac
if test ! -d "$pathcomp"; then
echo "mkdir $pathcomp" 1>&2
mkdir "$pathcomp" || lasterr=$?
if test ! -d "$pathcomp"; then
errstatus=$lasterr
fi
fi
pathcomp="$pathcomp/"
done
done
exit $errstatus
# mkinstalldirs ends here

215
nodist/dot_pruner Executable file
View File

@ -0,0 +1,215 @@
#!/usr/bin/perl -w
# $Id$
######################################################################
#
# Copyright 2005-2006 by Wilson Snyder <wsnyder@wsnyder.org>. This
# program is free software; you can redistribute it and/or modify it under
# the terms of either the GNU Lesser General Public License or the Perl
# Artistic License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
######################################################################
require 5.006_001;
use Getopt::Long;
use IO::File;
use Pod::Usage;
use Data::Dumper; $Data::Dumper::Indent=1;
use strict;
use vars qw ($Debug);
#======================================================================
our @Header;
our %Vertexes;
our %Edges;
our %User;
our %User2;
#======================================================================
# main
$Debug = 0;
my $opt_filename;
my $opt_circle;
autoflush STDOUT 1;
autoflush STDERR 1;
if (! GetOptions (
"help" => \&usage,
"debug" => \&debug,
"<>" => \&parameter,
"circle=s" => \$opt_circle,
)) {
usage();
}
dotread ($opt_filename);
circle($opt_circle) if $opt_circle;
simplify();
dotwrite();
#----------------------------------------------------------------------
sub usage {
print '$Id$ ', "\n";
pod2usage(-verbose=>2, -exitval => 2);
exit (1);
}
sub debug {
$Debug = 1;
}
sub parameter {
my $param = shift;
if (!$opt_filename) {
$opt_filename = $param;
} else {
die "%Error: Unknown parameter: $param\n";
}
}
#######################################################################
sub dotread {
my $filename = shift;
my $fh = IO::File->new($filename) or die "%Error: $! $filename,";
my $header = 1;
while (defined (my $line = $fh->getline)) {
if ($line =~ /^\t([a-zA-Z0-9_]+)\t(.*)$/) {
$header = 0;
$Vertexes{$1} = $2;
}
elsif ($line =~ /^\t([a-zA-Z0-9_]+)\s+->\s+([a-zA-Z0-9_]+)\s+(.*)$/) {
$Edges{$1}{$2} = $3;
}
elsif ($header) {
push @Header, $line;
}
}
}
######################################################################
sub simplify {
foreach my $ver (sort (keys %Vertexes)) {
$Vertexes{$ver} = _simplify($Vertexes{$ver});
}
foreach my $v1 (sort (keys %Edges)) {
foreach my $v2 (sort (keys %{$Edges{$v1}})) {
$Edges{$v1}{$v2} = _simplify($Edges{$v1}{$v2});
}
}
}
sub _simplify {
my $text = shift;
$text =~ s/__DOT__/./g;
return $text;
}
sub dotwrite {
foreach my $line (@Header) {
print "$line";
}
foreach my $ver (sort (keys %Vertexes)) {
print "\t$ver\t$Vertexes{$ver}\n";
}
foreach my $v1 (sort (keys %Edges)) {
foreach my $v2 (sort (keys %{$Edges{$v1}})) {
print "\t$v1 -> $v2\t$Edges{$v1}{$v2}\n";
}
}
print "}\n";
}
######################################################################
sub circle {
my $node = shift;
%User = ();
%User2 = ();
_circle_recurse($node, 1);
foreach my $ver (keys %Vertexes) {
if (!$User{$ver}) {
delete $Vertexes{$ver};
delete $Edges{$ver};
}
}
foreach my $v1 (sort (keys %Edges)) {
foreach my $v2 (sort (keys %{$Edges{$v1}})) {
if (!$Vertexes{$v2}) { delete $Edges{$v1}{$v2}; }
}
}
}
sub _circle_recurse {
my $node = $_[0];
my $level = $_[1];
$Vertexes{$node} or warn "%Warning: Can't find ref node $node\n";
$User{$node} = 1 if (($User2{$node}||0)==1);
return $User{$node} if $User2{$node};
$User2{$node} = 1;
my $r = 0;
foreach my $v2 (keys %{$Edges{$node}}) {
$r |= _circle_recurse($v2,$level++)||0;
}
$User{$node} = 1 if $r;
$User2{$node} = 2;
return $r;
}
#######################################################################
__END__
=pod
=head1 NAME
dot_pruner -
=head1 SYNOPSIS
dot_pruner *.log
=head1 DESCRIPTION
dd
=head1 ARGUMENTS
=over 4
=item --help
Displays this message and program version and exits.
=back
=head1 DISTRIBUTION
Copyright 2005-2006 by Wilson Snyder. This package is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License or the Perl Artistic License.
=head1 AUTHORS
Wilson Snyder <wsnyder@wsnyder.org>
=head1 SEE ALSO
=cut
######################################################################
### Local Variables:
### compile-command: "./dot_pruner | tee ~/d/a.dot"
### End:

362
nodist/vtree_importer Executable file
View File

@ -0,0 +1,362 @@
#!/usr/bin/perl -w
# $Id$
######################################################################
#
# Copyright 2005-2006 by Wilson Snyder <wsnyder@wsnyder.org>. This
# program is free software; you can redistribute it and/or modify it under
# the terms of either the GNU Lesser General Public License or the Perl
# Artistic License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
######################################################################
require 5.006_001;
use Getopt::Long;
use IO::File;
use Pod::Usage;
use Data::Dumper; $Data::Dumper::Indent=1;
use strict;
use vars qw ($Debug);
#======================================================================
our $Tree;
our %OpMap;
gentree();
#======================================================================
# main
$Debug = 0;
my $opt_filename;
autoflush STDOUT 1;
autoflush STDERR 1;
if (! GetOptions (
"help" => \&usage,
"debug" => \&debug,
"<>" => \&parameter,
)) {
usage();
}
vread ($opt_filename);
#print Dumper($Tree);
vwrite();
print '(query-replace-regexp "(\\([0-9a-z_]+\\))" "\\1" nil nil nil)',"\n";
#----------------------------------------------------------------------
sub usage {
print '$Id$ ', "\n";
pod2usage(-verbose=>2, -exitval => 2);
exit (1);
}
sub debug {
$Debug = 1;
}
sub parameter {
my $param = shift;
if (!$opt_filename) {
$opt_filename = $param;
} else {
die "%Error: Unknown parameter: $param\n";
}
}
#######################################################################
sub vread {
my $filename = shift;
my $fh = IO::File->new($filename) or die "%Error: $! $filename,";
my $lasthier="";
$Tree = {
op => 'NETLIST',
t => [[],[],[],[],[],],
};
my @stack;
$stack[1] = $Tree;
while (defined (my $line = $fh->getline)) {
if ($line =~ /^\s+(\S+):\s+(\S+)\s+0x\S+\s+{(\d+)}\s+w(\d+)\s+(.*)$/) {
my $hier = $1;
my $op = $2;
my $lineno = $3;
my $width = $4;
my $etc = $5;
$etc =~ s/__DOT__/./g;
$etc =~ s/__PVT__//g;
my $self = {
op => $op,
#width => $width,
#lineno => $lineno,
#line => $line,
etc => $etc,
args => [split(/[ \t]+/,$etc)],
t => [[],[],[],[],[],],
};
my @hiers = (1,split(/:/,$hier));
my $depth = $#hiers+1;
my $newchild = $hiers[$#hiers];
#print "DD $depth $newchild $op\n";
push @{$stack[$depth-1]->{t}[$newchild]}, $self;
$stack[$depth] = $self;
$lasthier = $hier;
#print " $lasthier\n";
#print Dumper($Tree);
}
}
}
######################################################################
our $Indent = 0;
use vars qw($Code_Self);
use vars qw($Avoid_Hex);
sub vwrite {
$Indent = 0;
print vwrite_rec($Tree);
}
sub vwrite_rec {
my $self = shift;
#print "/*$self->{op}*/";
my $code = $OpMap{$self->{op}} or die "%Error: No map for $self->{op},";
local $Code_Self = $self;
#print Dumper($self->{t}[3]),"\n";
&$code;
}
######################################################################
# Tree functions
sub p { print join("",@_); }
sub exists1 { return defined $Code_Self->{t}[1][0]; }
sub exists2 { return defined $Code_Self->{t}[2][0]; }
sub exists3 { return defined $Code_Self->{t}[3][0]; }
sub exists4 { return defined $Code_Self->{t}[4][0]; }
sub exists5 { return defined $Code_Self->{t}[5][0]; }
sub t1 { foreach my $r (@{$Code_Self->{t}[1]}) { vwrite_rec($r); } }
sub t2 { foreach my $r (@{$Code_Self->{t}[2]}) { vwrite_rec($r); } }
sub t3 { foreach my $r (@{$Code_Self->{t}[3]}) { vwrite_rec($r); } }
sub t4 { foreach my $r (@{$Code_Self->{t}[4]}) { vwrite_rec($r); } }
sub t5 { foreach my $r (@{$Code_Self->{t}[5]}) { vwrite_rec($r); } }
sub p1 { p "("; t1; p ")";}
sub p2 { p "("; t2; p ")";}
sub p3 { p "("; t3; p ")";}
sub p4 { p "("; t4; p ")";}
sub p5 { p "("; t5; p ")";}
sub a1 { p $Code_Self->{args}[0]; }
sub a2 { p $Code_Self->{args}[1]; }
sub a3 { p $Code_Self->{args}[2]; }
sub a4 { p $Code_Self->{args}[3]; }
sub a5 { p $Code_Self->{args}[4]; }
sub a6 { p $Code_Self->{args}[5]; }
sub a7 { p $Code_Self->{args}[6]; }
sub indentInc { $Indent+=2; }
sub indentDec { $Indent-=2; }
sub nl { p "\n"," "x$Indent; }
######################################################################
# nl is a newline
# p# indicates to add parens
# t# indicates tree reference
# a# indicates info from dump where n1 is the width.
sub gentree {
%OpMap = (
'NULLNODE' => sub { "" },
'NETLIST' => sub { nl;t1;t2;t3;t4;t5; },
'ACTIVE' => sub { p "always_act @(";t1;p ") begin";indentInc;nl;t2;t3;t4;t5;indentDec;p "end";nl; },
'ADD' => sub { p1;p " + ";p2; },
'ALWAYS' => sub { p "always @(";t1;p ") begin";indentInc;nl;t2;t3;t4;t5;indentDec;p "end";nl; },
'ALWAYSPOST' => sub { p "ALWAYSPOST what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'AND' => sub { p1;p " & ";p2; },
'ARRAYSEL' => sub { t1;p "[";t2;p "]"; },
'ASSIGN' => sub { t2;p " = ";t1;p ";";nl; },
'ASSIGNDLY' => sub { t2;p " <= ";t1;p ";";nl; },
'ASSIGNPOST' => sub { p "ASSIGNPOST what{";t1;p " = ";t2;p ";";nl; },
'ASSIGNPRE' => sub { p "ASSIGNPRE what{";t1;p " = ";t2;p ";";nl; },
'ASSIGNW' => sub { p "assign ";t2;p " = ";t1;p ";";nl; },
'ATTROF' => sub { p "ATTROF what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'BEGIN' => sub { p "begin";indentInc;nl;t1;t2;t3;t4;t5;indentDec;p "end";nl; },
'BITSEL' => sub { t1;local $Avoid_Hex=1; p "[";t2;p "]"; },
'CASE' => sub { p "CASE what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'CASEITEM' => sub { p "CASEITEM what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'CAST' => sub { p "CAST what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'CCALL' => sub { p "CCALL what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'CELL' => sub { a1;p " ";a7;p " (/*CELL*/);"; nl; },
'CFUNC' => sub { p "CFUNC what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'CHANGEDET' => sub { p "CHANGEDET what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'CINCLUDE' => sub { p "CINCLUDE what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'COMMENT' => sub { p "//COMMENT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl;nl; },
'CONCAT' => sub { p "{";p1;p ",";p2;p "}"; },
'CONDITIONAL' => sub { p1;p " ? ";p2;p " : ";p3; },
'CONST' => sub { p_const(); },
'COVER' => sub { p "COVER what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'CRETURN' => sub { p "CRETURN what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'CSTMT' => sub { p "CSTMT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'DEFPARAM' => sub { p "defparam ";p1;p " = ";p2;p ";";nl; },
'DISPLAY' => sub { p '$write("';p1;p "\",";p2;p3;p4;p5;p ");";nl; },
'DIV' => sub { p1;p " / ";p2; },
'EQ' => sub { p1;p " == ";p2; },
'EQCASE' => sub { p1;p " === ";p2; },
'EXTEND' => sub { t1; },
'EXTRACT' => sub { t1;local $Avoid_Hex=1; p "[";t2;p ":";t3;p "]"; },
'FINISH' => sub { p '$finish;';nl },
'FOR' => sub { p "for (";p1;p ",";p2;p ",";p3;p ") begin";indentInc;nl;p4;p5;indentDec;p "end";nl; },
'FUNC' => sub { p "FUNC what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'FUNCREF' => sub { p "FUNCREF what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'GT' => sub { p1;p " > ";p2; },
'GTE' => sub { p1;p " >= ";p2; },
'IF' => sub { p "if (";p1;p ") begin";indentInc;nl;t2;indentDec;if (exists3) {p "end else begin";indentInc;nl;t3;indentDec;} p "end"; nl; },
'INITARRAY' => sub { p "INITARRAY what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'INITIAL' => sub { p "initial begin";indentInc;nl;t1;t2;t3;t4;t5;indentDec;p "end";nl; },
'LOGAND' => sub { p1;p " && ";p2; },
'LOGNOT' => sub { p1;p " || ";p2; },
'LOGOR' => sub { p "!";p1; },
'LT' => sub { p1;p " < ";p2; },
'LTE' => sub { p1;p " <= ";p2; },
'MODDIV' => sub { p1;p " % ";p2; },
'MODULE' => sub { p "module ";a1;p " (/*AUTOARG*/);";indentInc;nl;t1;t2;t3;t4;t5;indentDec;nl;p "endmodule";nl; },
'MUL' => sub { p1;p " * ";p2; },
'NEQ' => sub { p1;p " != ";p2; },
'NEQCASE' => sub { p1;p " !== ";p2; },
'NOT' => sub { p " ~";p1; },
'OR' => sub { p1;p " | ";p2; },
'PIN' => sub { p ";p ";p1;p " (";p2;p "),";nl; },
'PORT' => sub { p ""; },
'PRAGMA' => sub { p "PRAGMA what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'RAND' => sub { p '$rand'; },
'RANGE' => sub { t1; local $Avoid_Hex=1; p "[";t2;p ":";t3;p "]"; },
'REDAND' => sub { p "&(";p1;p ")"; },
'REDOR' => sub { p "|(";p1;p ")"; },
'REDXNOR' => sub { p "~|(";p1;p ")"; },
'REDXOR' => sub { p "~^(";p1;p ")"; },
'REPLICATE' => sub { p "{";p1;p "{";p2;p "}}"; },
'SCCTOR' => sub { p "SCCTOR what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'SCHDR' => sub { p "SCHDR what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'SCIMP' => sub { p "SCIMP what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'SCINT' => sub { p "SCINT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'SCOPE' => sub { t1;t2;t3;t4;t5; },
'SENITEM' => sub { a1;p " ";t1; },
'SENTREE' => sub { t1;t2;t3;t4;t5; },
'SHIFTL' => sub { p1;p " << ";p2; },
'SHIFTR' => sub { p1;p " >> ";p2; },
'STOP' => sub { p '$stop;';nl; },
'SUB' => sub { p1;p " - ";p2; },
'TASK' => sub { p "TASK what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'TASKREF' => sub { p "TASKREF what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'TEXT' => sub { p "TEXT what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'TIME' => sub { p '$time'; },
'TOPSCOPE' => sub { t1;t2;t3;t4;t5; },
'TRACE' => sub { p "TRACE what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'UCFUNC' => sub { p '$c(';p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p ")"; },
'UCSTMT' => sub { p '$c(';p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p ");";nl; },
'UNARYMIN' => sub { p " -";p1; },
'VAR' => sub { p_var(); },
'VARPIN' => sub { p "VARPIN what{";p1;p ",";p2;p ",";p3;p ",";p4;p ",";p5;p "}";nl; },
'VARREF' => sub { a1; },
'VARSCOPE' => sub { },
'WHILE' => sub { t1; p "while (";t2;p ") begin";indentInc;nl;t3;t4;indentDec;p "end";nl; },
'WORDSEL' => sub { p1;p "[";p2;p ":";p3;p "]"; },
'XNOR' => sub { p1;p " ~^ ";p2; },
'XOR' => sub { p1;p " ^";p2; },
);
}
sub p_var {
my $self = $Code_Self;
if ($self->{etc} =~ /\[I\]/) {
print "input";
} elsif ($self->{etc} =~ /\[O\]/) {
print "output";
} else {
print "reg";
}
p "\t";
{
local $Avoid_Hex=1;
t1;
}
p "\t";
a1;
if (exists2()) {
p " = ";
t2;
}
p ";";
nl;
}
sub p_const {
my $v = $Code_Self->{args}[0];
if ($v =~ /\?32\?h(.*)$/
|| ($Avoid_Hex && $v =~ /^[0-9?]*h(.*)$/)) {
print hex $1;
} else {
print $v;
}
}
#######################################################################
__END__
=pod
=head1 NAME
vtree_importer -
=head1 SYNOPSIS
vtree_importer *.log
=head1 DESCRIPTION
dd
=head1 ARGUMENTS
=over 4
=item --help
Displays this message and program version and exits.
=back
=head1 DISTRIBUTION
Copyright 2005-2006 by Wilson Snyder. This package is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License or the Perl Artistic License.
=head1 AUTHORS
Wilson Snyder <wsnyder@wsnyder.org>
=head1 SEE ALSO
=cut
######################################################################
### Local Variables:
### compile-command: "./vtree_importer "
### End:

188
readme.texi Normal file
View File

@ -0,0 +1,188 @@
\input texinfo @c -*-texinfo-*-
@c %**start of header
$c $Id$
@setfilename readme.info
@settitle Verilator Installation
@c %**end of header
@c DESCRIPTION: TexInfo: DOCUMENT source run through texinfo to produce README file
@c Use 'make README' to produce the output file
@c Before release, run C-u C-c C-u C-a (texinfo-all-menus-update)
@node Top, Copyright, (dir), (dir)
@chapter Verilator
This is the Verilator Package.
@menu
* Copyright::
* Description::
* Obtaining Distribution::
* Directory Structure::
* Supported Systems::
* Installation::
* Limitations::
@end menu
@node Copyright, Description, Top, Top
@section Copyright
This package is Copyright 2003-2006 by Wilson Snyder @email{wsnyder@@wsnyder.org}.
You may distribute under the terms of either the GNU General Public License
or the Artistic License, as specified in the Perl README file.
This code is provided with no warranty of any kind, and is used entirely at
your own risk.
@node Description, Obtaining Distribution, Copyright, Top
@section Description
Verilator converts synthesizable (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://veripool.com.
The resulting files are then compiled with C++. The user writes a little
C++ wrapper file, which instantiates the top level module. This is
compiled in C++, and linked with the Verilated files.
The resulting executable will perform the actual simulation.
@node Obtaining Distribution, Directory Structure, Description, Top
@section Obtaining Distribution
The latest version is available at
@uref{http://veripool.com/verilator.htm}
Download the latest package from that site, and decompress.
@samp{gunzip verilator_version.tar.gz ; tar xvf verilator_version.tar}
@node Directory Structure, Supported Systems, Obtaining Distribution, Top
@section Directory Structure
The directories after de-taring are as follows:
@itemize @bullet
@item bin/verilator => Compiler Wrapper invoked on user Verilog code
@item include/ => Files that should be in your -I compiler path
@item include/verilated.cpp => Global routines to link into your simulator
@item include/verilated.h => Global headers
@item include/verilated.v => Stub defines for linting
@item include/verilated.mk => Common makefile
@item src/ => Translator source code
@item test_v => Example Verilog code for other test dirs
@item test_c => Example Verilog->C++ conversion
@item test_sc => Example Verilog->SystemC conversion
@item test_sp => Example Verilog->SystemPerl conversion
@item test_vcs => Example Verilog->VCS conversion (test the test)
@item test_verilated => Internal tests
@item test_regress => Internal tests
@end itemize
@node Supported Systems, Installation, Directory Structure, Top
@section Supported Systems
This version of verilator has been built and tested on:
@itemize @bullet
@item SuSE AMD64 i686-linux-2.6.5
@end itemize
Other users report success with Redhat Linux 2.4, Windows under
Cygwin, HPUX and Solaris. It should run with minor porting on any
Unix system.
@node Installation, Limitations, Supported Systems, Top
@section Installation
@enumerate
@item
If you will be using SystemC (vs straight C++ output), download
SystemC 2.0.1 from @url{http://www.systemc.org}. Follow their
installation instructions. As described in the System-Perl README,
you will need to set SYSTEMC and/or SYSTEMC_KIT to point to this
download. Also, set SYSTEMC_ARCH to the architecture name you used
with SystemC, generally 'linux' or 'cygwin'.
@item
If you will be using SystemC, download and install Verilog-Perl,
@url{http://search.cpan.org/search?module=Verilog::Language}.
@item
If you will be using SystemC, download and install System-Perl,
@url{http://search.cpan.org/search?module=SystemC::Netlist}. Note
you'll need to set a @samp{SYSTEMPERL} environment variable to point
to the downloaded kit (not the installed files.) Also, make sure to
do a @code{make sc_patch}.
@item
@code{cd} to the Verilator directory containing this README.
@item
Type @samp{./configure} to configure Verilator for your system.
@item
Type @samp{make} to compile Verilator.
On Cygwin (Windows) you may get a error about libperl.a not being
found. You need to copy your perl libraries as follows.
@enumerate
@item
Type @samp{perl -MExtUtils::Embed -e ldopts}
@item
It will show a directory name ending in /CORE. cd to that directory.
@item
@samp{cp libperl5_6_1.a libperl.a}
@item
@samp{cp libperl5_6_1.dll libperl.dll}
@item
@samp{cp libperl5_6_1.def libperl.def}
@end enumerate
@item
Type @samp{make test} to check the compilation.
You may get a error about the Bit::Vector perl package. You will need to install
it if you want the tests to pass. (Try @samp{make test_c} for a smaller test that
doesn't require it.)
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 unsigned long uint32_t;}.
If you get warnings, you might want to edit @samp{include/verilated.mk} to delete the
lines that define VK_CPPFLAGS_WALL.
@item
There is no installation at present; this package runs from the
distribution directory. Programs should set the environment variable
VERILATOR_ROOT to point to this distribution, then execute
$VERILATOR_ROOT/bin/verilator, which will find the path to all needed
files.
Verilator assumes you did a make in the SystemC kit directory. If not, you will need
to populate @samp{$SYSTEMC/include} and @samp{$SYSTEMC/lib-linux} appropriately.
If you will be modifying Verilator, you will probably want a second
stable copy of this kit for others to use while you experiment.
@item
Detailed documentation and the man page can be seen by running:
bin/verilator --help
or reading verilator.txt in the same directory as this README.
@end enumerate
@node Limitations, , Installation, Top
@section Limitations
See verilator.txt (or execute @samp{bin/verilator --help}) for limitations.

7
src/.cvsignore Normal file
View File

@ -0,0 +1,7 @@
*.old
config.h
Makefile
Makefile_obj
.objcache*
obj_*
config_rev.h

87
src/Makefile.in Normal file
View File

@ -0,0 +1,87 @@
# $Id$ */
#*****************************************************************************
#
# DESCRIPTION: Verilator: Makefile for verilog source
#
# Code available from: http://www.veripool.com/verilator
#
#*****************************************************************************
#
# Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# General Public License or the Perl Artistic License.
#
# Verilator is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#****************************************************************************/
#### Start of system configuration section. ####
srcdir = @srcdir@
VPATH = @srcdir@
PERL = @PERL@
#### End of system configuration section. ####
default: dbg opt
debug: dbg
optimize: opt
ifeq ($(OBJCACHE_HOSTS),)
ifneq ($(SLCHOOSED_HOST),)
ifeq ($(VERILATOR_AUTHOR_SITE),1)
export OBJCACHE_HOSTS := $(shell rschedule --no-allow-reserved --similar hostnames)
endif
endif
endif
ifeq ($(OBJCACHE_HOSTS),)
export OBJCACHE :=
else
export OBJCACHE_JOBS := -j $(shell objcache --jobs "$(OBJCACHE_HOSTS)")
export OBJCACHE := @objcache --read --write
endif
obj_opt:
mkdir $@
obj_dbg:
mkdir $@
.PHONY: ../verilator_bin ../verilator_bin_dbg
opt: ../verilator_bin
ifeq ($(VERILATOR_NO_OPT_BUILD),1) # Faster laptop development... One build
../verilator_bin: ../verilator_bin_dbg
cp -p $< $@
@-cp -p $<.exe $@.exe
else
../verilator_bin: obj_opt prefiles
cd obj_opt && $(MAKE) TGT=../$@ -f ../Makefile_obj serial
cd obj_opt && $(MAKE) $(OBJCACHE_JOBS) TGT=../$@ -f ../Makefile_obj
endif
dbg: ../verilator_bin_dbg
../verilator_bin_dbg: obj_dbg prefiles
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
cd obj_dbg && $(MAKE) $(OBJCACHE_JOBS) TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
prefiles::
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
prefiles:: config_rev.h
# This output goes into srcdir, as we need to distribute it as part of the kit.
config_rev.h: config_rev.pl .svn/entries
$(PERL) config_rev.pl . >$@
endif
maintainer-copy::
clean mostlyclean distclean maintainer-clean::
-rm -rf obj_* *.log *.dmp *.vpd core
-rm -f *.o *.d perlxsi.c *_gen_*
-rm -f *__gen*
-rm -f *.yy.* y.output y.tab.[cho] *_test
-rm -f .objcache*

247
src/Makefile_obj.in Normal file
View File

@ -0,0 +1,247 @@
# $Id$ -*- Makefile -*-
#*****************************************************************************
#
# DESCRIPTION: Verilator: Makefile for verilog source
#
# Code available from: http://www.veripool.com/verilator
#
#*****************************************************************************
#
# Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# General Public License or the Perl Artistic License.
#
# Verilator is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#****************************************************************************/
#### Start of system configuration section. ####
srcdir = ..
incdir = ../../include
PERL = @PERL@
CC = @CC@
CXX = @CXX@
LINK = @CXX@
#### End of system configuration section. ####
VPATH += . $(srcdir)
TGT = ../../verilator_bin
#################
ifeq ($(VL_DEBUG),)
# Optimize
LDFLAGS =
COPT = -O
else
# Debug
LDFLAGS =
COPT = -ggdb -DVL_DEBUG
# Debug & Profile:
#LDFLAGS = -pg -g
#COPT = -ggdb -pg -g
endif
#################
LEX = flex
LFLAGS = -d
YACC = bison -y
YFLAGS = -d -v
#LIBS += -ldl
#CCMALLOC = /usr/local/lib/ccmalloc-gcc.o -lccmalloc -ldl
LIBS = -lm -lfl
CPPFLAGSNOWALL = -MMD
CPPFLAGSNOWALL += -I. -I$(srcdir) -I$(incdir)
CPPFLAGSNOWALL += -DYYDEBUG # Required to get nice error messages
#CPPFLAGSNOWALL += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
CPPFLAGSNOWALL += $(COPT)
CPPFLAGS = $(CPPFLAGSNOWALL)
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
CPPFLAGS += -W -Wall -Wno-unused-parameter -Wno-char-subscripts -Werror
#CPPFLAGS += -pedantic-errors
endif
HEADERS = $(wildcard V*.h v*.h)
ASTGEN = $(srcdir)/astgen
######################################################################
#### Top level
all: make_info $(TGT)
make_info:
@echo " Compile flags: " $(CXX) ${CPPFLAGS}
clean mostlyclean distclean maintainer-clean::
-rm -f *.o *.d perlxsi.c *_gen_*
-rm -f *__gen*
-rm -f *.yy.* y.output y.tab.[cho] *_test
-rm -f obj_* .objcache*
distclean maintainer-clean:: clean
rm -f Makefile config.h
maintainer-clean::
maintainer-copy::
#### Top executable
RAW_OBJS = \
Verilator.o \
V3Active.o \
V3ActiveTop.o \
V3Assert.o \
V3AssertPre.o \
V3Ast.o \
V3AstNodes.o \
V3Begin.o \
V3Branch.o \
V3Broken.o \
V3Case.o \
V3Cast.o \
V3Changed.o \
V3Clean.o \
V3Clock.o \
V3Combine.o \
V3Const__gen.o \
V3Coverage.o \
V3Dead.o \
V3Delayed.o \
V3Depth.o \
V3Descope.o \
V3EmitC.o \
V3EmitCSyms.o \
V3EmitMk.o \
V3EmitV.o \
V3Error.o \
V3Expand.o \
V3File.o \
V3Gate.o \
V3GenClk.o \
V3Graph.o \
V3GraphAlg.o \
V3GraphAcyc.o \
V3GraphDfa.o \
V3GraphTest.o \
V3Hashed.o \
V3Inline.o \
V3Inst.o \
V3Life.o \
V3LifePost.o \
V3Link.o \
V3LinkCells.o \
V3LinkDot.o \
V3LinkLevel.o \
V3LinkResolve.o \
V3Localize.o \
V3Name.o \
V3Number.o \
V3Options.o \
V3Order.o \
V3Param.o \
V3PreShell.o \
V3Premit.o \
V3Scope.o \
V3Signed.o \
V3Split.o \
V3Stats.o \
V3StatsReport.o \
V3Subst.o \
V3Table.o \
V3Task.o \
V3Trace.o \
V3TraceDecl.o \
V3Unknown.o \
V3Unroll.o \
V3Width.o \
# Non-concatable
OBJS += \
V3Parse.o \
V3PreProc.o \
V3Read.o \
#### Linking
ifeq ($(VL_DEBUG),)
# Building with fewer objects to better optimize
#OBJS += V3__CONCAT.o
OBJS += $(RAW_OBJS)
else
OBJS += $(RAW_OBJS)
endif
V3__CONCAT.cpp: $(addsuffix .cpp, $(basename $(RAW_OBJS)))
$(PERL) $(srcdir)/../bin/verilator_includer $^ > $@
$(TGT): V3Ast__gen_classes.h $(OBJS)
@echo " Linking $@..."
-rm -rf $@ $@.exe
${LINK} ${LDFLAGS} -o $@ $(OBJS) $(CCMALLOC) ${LIBS}
@-cp $@.exe $@
# ok if cp failes on linux, it's there to insure make works on NT
V3Number_test: V3Number_test.o
${LINK} ${LDFLAGS} -o $@ $^ ${LIBS}
#### Modules
%__gen.cpp: %.cpp $(ASTGEN)
$(PERL) $(ASTGEN) -I$(srcdir) $*.cpp
%.o: %.cpp
$(OBJCACHE) ${CXX} ${CPPFLAGS} -c $<
%.o: %.c
$(OBJCACHE) ${CC} ${CPPFLAGS} -c $<
V3Read.o: V3Read.cpp V3Lexer.yy.cpp
$(OBJCACHE) ${CXX} ${CPPFLAGSNOWALL} -c $<
V3Parse.o: V3Parse.cpp y.tab.c
$(OBJCACHE) ${CXX} ${CPPFLAGSNOWALL} -c $<
V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
$(OBJCACHE) ${CXX} ${CPPFLAGSNOWALL} -c $<
#### Generated files
# Target rule called before parallel build to make generated files
serial:: V3Ast__gen_classes.h
V3Ast__gen_classes.h : $(ASTGEN) V3Ast.h V3AstNodes.h
$(PERL) $(ASTGEN) -I$(srcdir) --classes
y.tab.c y.tab.h: verilog.y $(HEADERS)
@echo "If you get errors from verilog.y below, try upgrading bison to version 1.875 or newer."
${YACC} ${YFLAGS} $<
V3Lexer_pregen.yy.cpp: verilog.l y.tab.h $(HEADERS)
${LEX} ${LFLAGS} -o$@ $<
V3Lexer.yy.cpp: V3Lexer_pregen.yy.cpp
$(PERL) $(srcdir)/flexfix <$< >$@
V3PreLex_pregen.yy.cpp: V3PreLex.l $(HEADERS)
${LEX} ${LFLAGS} -o$@ $<
V3PreLex.yy.cpp: V3PreLex_pregen.yy.cpp
$(PERL) $(srcdir)/flexfix <$< >$@
######################################################################
######################################################################
DEPS := $(wildcard *.d)
ifneq ($(DEPS),)
include $(DEPS)
endif

340
src/V3Active.cpp Normal file
View File

@ -0,0 +1,340 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Break always into sensitivity active domains
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Active's Transformations:
//
// Note this can be called multiple times.
// Create a IACTIVE(initial), SACTIVE(combo)
// ALWAYS: Remove any-edges from sense list
// If no POS/NEG in senselist, Fold into SACTIVE(combo)
// Else fold into SACTIVE(sequent).
// OPTIMIZE: When support async clocks, fold into that active if possible
// INITIAL: Move into IACTIVE
// WIRE: Move into SACTIVE(combo)
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <map>
#include <algorithm>
#include <vector>
#include "V3Global.h"
#include "V3Active.h"
#include "V3Ast.h"
//***** See below for main transformation engine
//######################################################################
// Collect existing active names
class ActiveBaseVisitor : public AstNVisitor {
protected:
//int debug() { return 9; }
};
class ActiveNamer : public ActiveBaseVisitor {
private:
typedef std::map<string,AstActive*> ActiveNameMap;
// STATE
AstScope* m_scopep; // Current scope to add statement to
AstActive* m_iActivep; // For current scope, the IActive we're building
AstActive* m_cActivep; // For current scope, the SActive(combo) we're building
vector<AstActive*> m_activeVec; // List of sensitive actives, for folding
// METHODS
void addActive(AstActive* nodep) {
if (!m_scopep) nodep->v3fatalSrc("NULL scope");
m_scopep->addActivep(nodep);
}
// VISITORS
virtual void visit(AstScope* nodep, AstNUser*) {
m_scopep = nodep;
m_iActivep = NULL;
m_cActivep = NULL;
m_activeVec.clear();
nodep->iterateChildren(*this);
// Don't clear scopep, the namer persists beyond this visit
}
virtual void visit(AstSenTree* nodep, AstNUser*) {
// Sort sensitivity list
nodep->sortSenses();
}
// Empty visitors, speed things up
virtual void visit(AstNodeStmt* nodep, AstNUser*) { }
//--------------------
// Default
virtual void visit(AstNode* nodep, AstNUser*) {
// Default: Just iterate
nodep->iterateChildren(*this);
}
// METHODS
public:
AstScope* scopep() { return m_scopep; }
AstActive* getCActive(FileLine* fl) {
if (!m_cActivep) {
m_cActivep = new AstActive(fl, "combo",
new AstSenTree(fl, new AstSenItem(fl,AstSenItem::Combo())));
m_cActivep->sensesStorep(m_cActivep->sensesp());
addActive(m_cActivep);
}
return m_cActivep;
}
AstActive* getIActive(FileLine* fl) {
if (!m_iActivep) {
m_iActivep = new AstActive(fl, "initial",
new AstSenTree(fl, new AstSenItem(fl,AstSenItem::Initial())));
m_iActivep->sensesStorep(m_iActivep->sensesp());
addActive(m_iActivep);
}
return m_iActivep;
}
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
// Return a sentree in this scope that matches given sense list.
// Not the fastest, but scopes tend to have few clocks
AstActive* activep = NULL;
//sitemsp->dumpTree(cout," Lookingfor: ");
for (vector<AstActive*>::iterator it = m_activeVec.begin(); it!=m_activeVec.end(); ++it) {
activep = *it;
if (activep) { // Not deleted
// Compare the list
AstSenTree* asenp = activep->sensesp();
if (asenp->sameTree(sensesp)) {
UINFO(8," Found ACTIVE "<<activep<<endl);
goto found;
}
}
activep = NULL;
}
found:
// Not found, form a new one
if (!activep) {
AstSenTree* newsenp = sensesp->cloneTree(false)->castSenTree();
activep = new AstActive(fl, "sequent", newsenp);
activep->sensesStorep(activep->sensesp());
UINFO(8," New ACTIVE "<<activep<<endl);
// Form the sensitivity list
addActive(activep);
m_activeVec.push_back(activep);
// Note actives may have also been added above in the Active visitor
}
return activep;
}
public:
// CONSTUCTORS
ActiveNamer() {}
virtual ~ActiveNamer() {}
void main(AstScope* nodep) {
nodep->accept(*this);
}
};
//######################################################################
// Active AssignDly replacement functions
class ActiveDlyVisitor : public ActiveBaseVisitor {
private:
// VISITORS
virtual void visit(AstAssignDly* nodep, AstNUser*) {
// Convert to a non-delayed assignment
UINFO(5," ASSIGNDLY "<<nodep<<endl);
nodep->v3warn(COMBDLY,"Delayed assignments (<=) in non-clocked (non flop or latch) blocks should be non-delayed assignments (=).");
AstNode* newp = new AstAssign (nodep->fileline(),
nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack());
nodep->replaceWith(newp);
nodep->deleteTree(); nodep = NULL;
}
// Empty visitors, speed things up
virtual void visit(AstNodeMath* nodep, AstNUser*) {}
//--------------------
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
ActiveDlyVisitor(AstNode* nodep) {
nodep->accept(*this);
}
virtual ~ActiveDlyVisitor() {}
};
//######################################################################
// Active class functions
class ActiveVisitor : public ActiveBaseVisitor {
private:
// NODE STATE
// STATE
ActiveNamer m_namer; // Tracking of active names
AstCFunc* m_scopeFinalp; // Final function for this scope
// VISITORS
virtual void visit(AstScope* nodep, AstNUser*) {
// Create required actives and add to scope
UINFO(4," SCOPE "<<nodep<<endl);
// Clear last scope's names, and collect this scope's existing names
m_namer.main(nodep);
m_scopeFinalp = NULL;
nodep->iterateChildren(*this);
}
virtual void visit(AstActive* nodep, AstNUser*) {
// Actives are being formed, so we can ignore any already made
}
virtual void visit(AstInitial* nodep, AstNUser*) {
// Relink to IACTIVE, unless already under it
UINFO(4," INITIAL "<<nodep<<endl);
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
// Relink to CACTIVE, unless already under it
UINFO(4," ASSIGNW "<<nodep<<endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstAssignW* nodep, AstNUser*) {
// Relink to CACTIVE, unless already under it
UINFO(4," ASSIGNW "<<nodep<<endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstFinal* nodep, AstNUser*) {
// Relink to CFUNC for the final
UINFO(4," FINAL "<<nodep<<endl);
if (!nodep->bodysp()) { // Empty, Kill it.
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
return;
}
if (!m_scopeFinalp) {
m_scopeFinalp = new AstCFunc(nodep->fileline(), "_final", m_namer.scopep());
m_scopeFinalp->dontCombine(true);
m_scopeFinalp->formCallTree(true);
m_scopeFinalp->slow(true);
m_namer.scopep()->addActivep(m_scopeFinalp);
}
nodep->unlinkFrBack();
m_scopeFinalp->addStmtsp(new AstComment(nodep->fileline(), nodep->typeName()));
m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext());
nodep->deleteTree(); nodep = NULL;
}
virtual void visit(AstAlways* nodep, AstNUser*) {
// Move always to appropriate ACTIVE based on its sense list
UINFO(4," ALW "<<nodep<<endl);
//if (debug()>=9) nodep->dumpTree(cout," Alw: ");
if (!nodep->bodysp()) {
// Empty always. Kill it.
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
return;
}
if (nodep->sensesp() && nodep->sensesp()->sensesp() && nodep->sensesp()->sensesp()->isNever()) {
// Never executing. Kill it.
if (nodep->sensesp()->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
return;
}
// Read sensitivitues
bool combo = false;
bool sequent = false;
if (nodep->sensesp()) {
for (AstSenItem* nextp, *senp = nodep->sensesp()->sensesp(); senp; senp=nextp) {
nextp = senp->nextp()->castSenItem();
if (senp->edgeType() == AstEdgeType::ANYEDGE) {
combo = true;
// Delete the sensitivity
// We'll add it as a generic COMBO SenItem in a moment.
senp->unlinkFrBack()->deleteTree(); senp=NULL;
} else if (senp->varrefp()) {
if (senp->varrefp()->width()>1) senp->v3error("Unsupported: Non-single bit wide signal pos/negedge sensitivity: "
<<senp->varrefp()->prettyName());
sequent = true;
senp->varrefp()->varp()->usedClock(true);
}
}
}
if (!combo && !sequent) combo=true; // If no list, Verilog 2000: always @ (*)
#ifndef NEW_ORDERING
if (combo && sequent) {
nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity (no edge) sensitive activity list");
sequent = false;
}
#endif
AstActive* wantactivep = NULL;
if (combo && !sequent) {
// Combo: Relink to ACTIVE(combo)
wantactivep = m_namer.getCActive(nodep->fileline());
} else {
// Sequential: Build a ACTIVE(name)
// OPTIMIZE: We could substitute a constant for things in the sense list, for example
// always (posedge RESET) { if (RESET).... } we know RESET is true.
// Summarize a long list of combo inputs as just "combo"
if (combo) nodep->sensesp()->addSensesp
(new AstSenItem(nodep->fileline(),AstSenItem::Combo()));
wantactivep = m_namer.getActive(nodep->fileline(), nodep->sensesp());
}
// Delete sensitivity list
if (AstNode* oldsense = nodep->sensesp()) {
oldsense->unlinkFrBackWithNext()->deleteTree(); oldsense=NULL;
}
// Move node to new active
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
// Warn and/or convert any delayed assignments
if (combo && !sequent) {
ActiveDlyVisitor dlyvisitor (nodep);
}
}
// Empty visitors, speed things up
virtual void visit(AstNodeMath* nodep, AstNUser*) {}
virtual void visit(AstVarScope* nodep, AstNUser*) {}
//--------------------
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
ActiveVisitor(AstNode* nodep) {
m_scopeFinalp = NULL;
nodep->accept(*this);
}
virtual ~ActiveVisitor() {}
};
//######################################################################
// Active class functions
void V3Active::activeAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
ActiveVisitor visitor (nodep);
}

35
src/V3Active.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Break always into sensitivity block domains
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3ACTIVE_H_
#define _V3ACTIVE_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Active {
public:
static void activeAll(AstNetlist* nodep);
};
#endif // Guard

148
src/V3ActiveTop.cpp Normal file
View File

@ -0,0 +1,148 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Break always into sensitivity active domains
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Active's Transformations:
//
// Note this can be called multiple times.
// Across all ACTIVES
// SenTrees are now under each ACTIVE statement, we want them global:
// Find SenTree in under global TopScope, or create it there
// Move SenTree the global SenTree
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include <vector>
#include "V3Global.h"
#include "V3ActiveTop.h"
#include "V3Ast.h"
#include "V3SenTree.h"
//######################################################################
// Active class functions
class ActiveTopVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist
// AstNode::user() bool. True if processed
// STATE
AstTopScope* m_topscopep; // Top scope for adding sentrees under
SenTreeFinder m_finder; // Find global sentree's and add them
//int debug() { return 9; }
// VISITORS
virtual void visit(AstTopScope* nodep, AstNUser*) {
m_topscopep = nodep;
m_finder.main(m_topscopep);
nodep->iterateChildren(*this);
m_topscopep = NULL;
}
virtual void visit(AstModule* nodep, AstNUser*) {
// Create required actives and add to module
// We can start ordering at a module, or a scope
UINFO(4," MOD "<<nodep<<endl);
nodep->iterateChildren(*this);
}
virtual void visit(AstActive* nodep, AstNUser*) {
UINFO(4," ACTIVE "<<nodep<<endl);
AstSenTree* sensesp = nodep->sensesp();
if (!sensesp) nodep->v3fatalSrc("NULL");
sensesp->sortSenses(); // Remove duplicate clocks and such
if (sensesp->sensesp() && sensesp->sensesp()->isNever()) {
// Never executing. Kill it.
if (sensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated.");
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
return;
}
// Copy combo tree to settlement tree with duplicated statements
if (sensesp->hasCombo()) {
AstSenTree* newsentreep
= new AstSenTree (nodep->fileline(),
new AstSenItem (nodep->fileline(), AstSenItem::Settle()));
AstActive* newp = new AstActive(nodep->fileline(),"settle", newsentreep);
newp->sensesStorep(newsentreep);
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
nodep->addNextHere(newp);
}
// Move the SENTREE for each active up to the global level.
// This way we'll easily see what clock domains are identical
AstSenTree* wantp = m_finder.getSenTree(nodep->fileline(), sensesp);
UINFO(4," lookdone\n");
if (wantp != sensesp) {
// Move the active's contents to the other active
UINFO(4," merge active "<<sensesp<<" into "<<wantp<<endl);
if (nodep->sensesStorep()) {
if (sensesp != nodep->sensesStorep()) nodep->v3fatalSrc("sensesStore should have been deleted earlier if different\n");
sensesp->unlinkFrBack();
// There may be other references to same sense tree,
// we'll be removing all references when we get to them,
// but don't dangle our pointer yet!
pushDeletep(sensesp);
}
nodep->sensesp(wantp);
}
// No need to do statements under it, they're already moved.
//nodep->iterateChildren(*this);
}
virtual void visit(AstInitial* nodep, AstNUser*) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAssignW* nodep, AstNUser*) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAlways* nodep, AstNUser*) {
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstFinal* nodep, AstNUser*) {
nodep->v3fatalSrc("Node should have been deleted");
}
// Empty visitors, speed things up
virtual void visit(AstNodeMath* nodep, AstNUser*) {}
virtual void visit(AstVarScope* nodep, AstNUser*) {}
//--------------------
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
ActiveTopVisitor(AstNetlist* nodep) {
m_topscopep = NULL;
AstNode::userClearTree();
nodep->accept(*this);
}
virtual ~ActiveTopVisitor() {}
};
//######################################################################
// Active class functions
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
ActiveTopVisitor visitor (nodep);
}

35
src/V3ActiveTop.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Break always into sensitivity block domains
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3ACTIVETOP_H_
#define _V3ACTIVETOP_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3ActiveTop {
public:
static void activeTopAll(AstNetlist* nodep);
};
#endif // Guard

216
src/V3Assert.cpp Normal file
View File

@ -0,0 +1,216 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Collect and print statistics
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2005-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <map>
#include <iomanip>
#include "V3Global.h"
#include "V3Assert.h"
#include "V3Ast.h"
#include "V3GraphDfa.h"
#include "V3Stats.h"
//######################################################################
// Assert class functions
class AssertVisitor : public AstNVisitor {
private:
// NODE STATE/TYPES
// Cleared on netlist
// AstNode::user() -> bool. True if processed
// STATE
AstModule* m_modp; // Last module
V3Double0 m_statAsCover; // Statistic tracking
V3Double0 m_statAsPsl; // Statistic tracking
V3Double0 m_statAsFull; // Statistic tracking
// METHODS
AstNode* newFireAssert(AstNode* nodep, const string& message) {
AstNode* bodysp = new AstDisplay
(nodep->fileline(), '\0',
(string("[%0t] %%Error: ")+nodep->fileline()->filebasename()
+":"+cvtToStr(nodep->fileline()->lineno())
+": Assertion failed in %m"
+((message != "")?": ":"")+message
+"\\n"),
NULL,
new AstTime(nodep->fileline()));
bodysp->addNext(new AstStop (nodep->fileline()));
// Add a internal if to check assertions are on.
// Don't make this a AND term, as it's unlikely to need to test this.
bodysp = new AstIf (nodep->fileline(),
new AstCMath(nodep->fileline(), "Verilated::assertOn()", 1),
bodysp, NULL);
return bodysp;
}
void newAssertion(AstNode* nodep, AstNode* propp, AstSenTree* sentreep, const string& message) {
propp->unlinkFrBack();
sentreep->unlinkFrBack();
//
AstNode* bodysp = NULL;
bool selfDestruct = false;
if (AstPslCover* snodep = nodep->castPslCover()) {
if (!v3Global.opt.coverageUser()) {
selfDestruct = true;
} else {
// V3Coverage assigned us a bucket to increment.
AstCoverInc* covincp = snodep->coverincp()->castCoverInc();
if (!covincp) snodep->v3fatalSrc("Missing coverage in PSL");
covincp->unlinkFrBack();
if (message!="") covincp->declp()->comment(message);
bodysp = covincp;
}
} else if (nodep->castPslAssert()) {
bodysp = newFireAssert(nodep,message);
// We assert the property is always true... so report when it fails
// (Note this is opposite the behavior of coverage statements.)
//FIX 'never' operator: not hold in current or any future cycle
propp = new AstLogNot (nodep->fileline(), propp);
} else {
nodep->v3fatalSrc("Unknown node type");
}
AstIf* ifp = new AstIf (nodep->fileline(), propp, bodysp, NULL);
bodysp = ifp;
if (nodep->castPslAssert()) ifp->branchPred(AstBranchPred::UNLIKELY);
//
AstNode* newp = new AstAlways (nodep->fileline(),
sentreep,
bodysp);
// Install it
if (selfDestruct) {
// Delete it after making the tree. This way we can tell the user
// if it wasn't constructed nicely or has other errors without needing --coverage.
newp->deleteTree();
nodep->unlinkFrBack();
} else {
nodep->replaceWith(newp);
}
// Bye
pushDeletep(nodep); nodep=NULL;
}
// VISITORS //========== Case assertions
virtual void visit(AstCase* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (!nodep->user()) {
nodep->user(true);
bool has_default=false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
if (itemp->isDefault()) has_default=true;
}
if (nodep->fullPragma()) {
// Simply need to add a default if there isn't one already
m_statAsFull++;
if (!has_default) {
nodep->addItemsp(new AstCaseItem(nodep->fileline(), NULL/*DEFAULT*/,
newFireAssert(nodep, "synthesis full_case, but non-match found")));
}
}
if (nodep->parallelPragma()) {
// Need to check that one, and only one of the case items match at any moment
// If there's a default, we allow none to match, else exactly one must match
m_statAsFull++;
if (!has_default && !nodep->itemsp()) {
// Not parallel, but harmlessly so.
} else {
AstNode* propp = NULL;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
AstNode* onep = new AstEq(icondp->fileline(),
nodep->exprp()->cloneTree(false),
icondp->cloneTree(false));
if (propp) propp = new AstConcat(icondp->fileline(), onep, propp);
else propp = onep;
}
}
AstNode* ohot = (has_default
? (new AstOneHot0(nodep->fileline(), propp))->castNode()
: (new AstOneHot (nodep->fileline(), propp))->castNode());
AstIf* ifp = new AstIf (nodep->fileline(),
new AstLogNot (nodep->fileline(), ohot),
newFireAssert(nodep, "synthesis parallel_case, but multiple matches found"),
NULL);
ifp->branchPred(AstBranchPred::UNLIKELY);
nodep->addNotParallelp(ifp);
}
}
}
}
// VISITORS //========== Statements
virtual void visit(AstPslCover* nodep, AstNUser*) {
nodep->iterateChildren(*this);
newAssertion(nodep, nodep->propp(), nodep->sentreep(), nodep->name()); nodep=NULL;
m_statAsCover++;
}
virtual void visit(AstPslAssert* nodep, AstNUser*) {
nodep->iterateChildren(*this);
newAssertion(nodep, nodep->propp(), nodep->sentreep(), nodep->name()); nodep=NULL;
m_statAsPsl++;
}
virtual void visit(AstModule* nodep, AstNUser*) {
m_modp = nodep;
//
nodep->iterateChildren(*this);
// Reset defaults
m_modp = NULL;
}
// VISITORS //========== Temporal Layer
// VISITORS //========== Boolean Layer
virtual void visit(AstPslBool* nodep, AstNUser*) {
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTRUCTORS
AssertVisitor(AstNetlist* nodep) {
m_modp = NULL;
// Process
AstNode::userClearTree(); // userp() used on entire tree
nodep->accept(*this);
}
virtual ~AssertVisitor() {
V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl);
V3Stats::addStat("Assertions, cover statements", m_statAsCover);
V3Stats::addStat("Assertions, full/parallel case", m_statAsFull);
}
};
//######################################################################
// Top Assert class
void V3Assert::assertAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
AssertVisitor visitor (nodep);
}

35
src/V3Assert.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Assertion expansion
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2005-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3ASSERT_H_
#define _V3ASSERT_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Assert {
public:
static void assertAll(AstNetlist* nodep);
};
#endif // Guard

125
src/V3AssertPre.cpp Normal file
View File

@ -0,0 +1,125 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Collect and print statistics
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2005-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// Pre steps:
// Attach clocks to each assertion
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <map>
#include <iomanip>
#include "V3Global.h"
#include "V3AssertPre.h"
#include "V3Ast.h"
//######################################################################
// Assert class functions
class AssertPreVisitor : public AstNVisitor {
// Removes clocks and other pre-optimizations
// Eventually inlines calls to sequences, properties, etc.
// We're not parsing the tree, or anything more complicated.
private:
// NODE STATE/TYPES
// STATE
// Reset each module:
AstSenItem* m_seniDefaultp; // Default sensitivity (from AstDefClock)
// Reset each assertion:
AstSenItem* m_senip; // Last sensitivity
int debug() { return 0; }
// METHODS
AstSenTree* newSenTree(AstNode* nodep) {
// Create sentree based on clocked or default clock
// Return NULL for always
AstSenTree* newp = NULL;
AstSenItem* senip = m_senip ? m_senip : m_seniDefaultp;
if (senip) newp = new AstSenTree(nodep->fileline(), senip->cloneTree(true));
if (!senip) nodep->v3error("Unsupported: Unclocked assertion");
return newp;
}
// VISITORS //========== Statements
virtual void visit(AstPslDefClock* nodep, AstNUser*) {
nodep->iterateChildren(*this);
// Store the new default clock, reset on new module
m_seniDefaultp = nodep->sensesp();
// Trash it
nodep->unlinkFrBack();
pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstPslCover* nodep, AstNUser*) {
// Prep
m_senip = NULL;
nodep->iterateChildren(*this);
nodep->sentreep(newSenTree(nodep));
m_senip = NULL;
}
virtual void visit(AstPslAssert* nodep, AstNUser*) {
// Prep
m_senip = NULL;
nodep->iterateChildren(*this);
nodep->sentreep(newSenTree(nodep));
m_senip = NULL;
}
virtual void visit(AstPslClocked* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (m_senip) {
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion\n");
}
// Unlink and just keep a pointer to it, convert to sentree as needed
AstNode* blockp = nodep->propp()->unlinkFrBack();
m_senip = nodep->sensesp();
nodep->replaceWith(blockp);
pushDeletep(nodep); nodep=NULL;
}
virtual void visit(AstModule* nodep, AstNUser*) {
nodep->iterateChildren(*this);
// Reset defaults
m_seniDefaultp = NULL;
}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTRUCTORS
AssertPreVisitor(AstNetlist* nodep) {
m_senip = NULL;
m_seniDefaultp = NULL;
// Process
nodep->accept(*this);
}
virtual ~AssertPreVisitor() {}
};
//######################################################################
// Top Assert class
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
AssertPreVisitor visitor (nodep);
}

35
src/V3AssertPre.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Assertion pre-expansion
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2005-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3ASSERTPRE_H_
#define _V3ASSERTPRE_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3AssertPre {
public:
static void assertPreAll(AstNetlist* nodep);
};
#endif // Guard

818
src/V3Ast.cpp Normal file
View File

@ -0,0 +1,818 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Ast node structures
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include <stdio.h>
#include <stdarg.h>
#include <fstream>
#include <iomanip>
#include <memory>
#include "V3Ast.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3Broken.h"
//======================================================================
// Statics
uint64_t AstNode::s_editCntGbl=0;
uint64_t AstNode::s_editCntLast=0;
// To allow for fast clearing of all user pointers, we keep a "timestamp"
// along with each userp, and thus by bumping this count we can make it look
// as if we iterated across the entire tree to set all the userp's to null.
int AstNode::s_cloneCntGbl=0;
int AstNode::s_userCntGbl=0;
int AstNode::s_user2CntGbl=0;
int AstNode::s_user3CntGbl=0;
int AstNode::s_user4CntGbl=0;
int AstNode::s_user5CntGbl=0;
//######################################################################
// V3AstType
ostream& operator<<(ostream& os, AstType rhs);
//######################################################################
// Creators
void AstNode::init() {
editCountInc();
m_fileline = NULL;
m_nextp = NULL;
m_backp = NULL;
m_headtailp = this; // When made, we're a list of only a single element
m_op1p = NULL;
m_op2p = NULL;
m_op3p = NULL;
m_op4p = NULL;
m_iterpp = NULL;
m_clonep = NULL;
m_cloneCnt = 0;
// Attributes
m_signed = false;
m_width = 0;
m_widthMin = 0;
m_userp = NULL;
m_userCnt = 0;
m_user2p = NULL;
m_user2Cnt = 0;
m_user3p = NULL;
m_user3Cnt = 0;
m_user4p = NULL;
m_user4Cnt = 0;
m_user5p = NULL;
m_user5Cnt = 0;
}
string AstNode::shortName() const {
string pretty = name();
string::size_type pos;
while ((pos=pretty.find("__PVT__")) != string::npos) {
pretty.replace(pos, 7, "");
}
return pretty;
}
string AstNode::prettyName(const string& namein) {
string pretty = namein;
string::size_type pos;
while ((pos=pretty.find("__DOT__")) != string::npos) {
pretty.replace(pos, 7, ".");
}
while ((pos=pretty.find("__PVT__")) != string::npos) {
pretty.replace(pos, 7, "");
}
if (pretty.substr(0,4) == "TOP.") pretty.replace(0,4,"");
return pretty;
}
int AstNode::widthPow2() const {
// I.e. width 30 returns 32, width 32 returns 32.
uint32_t width = this->width();
for (int p2=30; p2>=0; p2--) {
if (width > (1UL<<p2)) return (1UL<<(p2+1));
}
return 1;
}
//######################################################################
// Insertion
inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next) {
#ifdef VL_DEBUG
// Called on all major tree changers.
// Only for use for those really nasty bugs relating to internals
// Note this may be null.
//if (debug()) {
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
// v3Global.rootp()->dumpTree(cout,"-treeChange: ");
// if (next||1) this->dumpTreeAndNext(cout, prefix);
// else this->dumpTree(cout, prefix);
// this->checkTree();
// v3Global.rootp()->checkTree();
//}
#endif
}
AstNode* AstNode::addNext(AstNode* newp) {
// Add to m_nextp, returns this
UASSERT(newp,"Null item passed to addNext\n");
this->debugTreeChange("-addNextThs: ", __LINE__, false);
newp->debugTreeChange("-addNextNew: ", __LINE__, true);
if (this == NULL) {
return (newp);
} else {
// Find end of old list
AstNode* oldtailp = this;
if (oldtailp->m_nextp) {
if (oldtailp->m_headtailp) {
oldtailp = oldtailp->m_headtailp; // This=beginning of list, jump to end
UASSERT(!oldtailp->m_nextp, "Node had next, but headtail says it shouldn't");
} else {
// Though inefficent, we are occasionally passed a addNext in the middle of a list.
while (oldtailp->m_nextp != NULL) oldtailp = oldtailp->m_nextp;
}
}
// Link it in
oldtailp->m_nextp = newp;
newp->m_backp = oldtailp;
// New tail needs the head
AstNode* newtailp = newp->m_headtailp;
AstNode* headp = oldtailp->m_headtailp;
oldtailp->m_headtailp = NULL; // May be written again as new head
newp->m_headtailp = NULL; // May be written again as new tail
newtailp->m_headtailp = headp;
headp->m_headtailp = newtailp;
newp->editCountInc();
if (oldtailp->m_iterpp) *(oldtailp->m_iterpp) = newp; // Iterate on new item
}
this->debugTreeChange("-addNextOut:", __LINE__, true);
return this;
}
AstNode* AstNode::addNextNull(AstNode* newp) {
if (!newp) return this;
return addNext(newp);
}
void AstNode::addNextHere(AstNode* newp) {
// Add to m_nextp on exact node passed, not at the end.
// This could be at head, tail, or both (single)
// New could be head of single node, or list
UASSERT(newp,"Null item passed to addNext");
UASSERT(this,"Null base node");
UASSERT(newp->backp()==NULL,"New node (back) already assigned?");
this->debugTreeChange("-addHereThs: ", __LINE__, false);
newp->debugTreeChange("-addHereNew: ", __LINE__, true);
newp->editCountInc();
AstNode* addlastp = newp->m_headtailp; // Last node in list to be added
UASSERT(!addlastp->m_nextp, "Headtailp tail isn't at the tail");
// Forward links
AstNode* oldnextp = this->m_nextp;
this->m_nextp = newp;
addlastp->m_nextp = oldnextp; // Perhaps null if 'this' is not list
// Backward links
if (oldnextp) oldnextp->m_backp = addlastp;
newp->m_backp = this;
// Head/tail
AstNode* oldheadtailp = this->m_headtailp;
// (!oldheadtailp) // this was&is middle of list
// (oldheadtailp==this && !oldnext)// this was head AND tail (one node long list)
// (oldheadtailp && oldnextp) // this was&is head of list of not just one node, not tail
// (oldheadtailp && !oldnextp) // this was tail of list, might also be head of one-node list
//
newp->m_headtailp = NULL; // Not at head any longer
addlastp->m_headtailp = NULL; // Presume middle of list
// newp might happen to be head/tail after all, if so will be set again below
if (oldheadtailp) { // else in middle of list, no change
if (oldheadtailp==this) { // this was one node
this->m_headtailp = addlastp; // Was head/tail, now a tail
addlastp->m_headtailp = oldheadtailp; // Tail needs to remember head (or NULL)
} else if (!oldnextp) { // this was tail
this->m_headtailp = NULL; // No longer a tail
oldheadtailp->m_headtailp = addlastp; // Head gets new tail
addlastp->m_headtailp = oldheadtailp; // Tail needs to remember head (or NULL)
} // else is head, and we're inserting into the middle, so no other change
}
if (this->m_iterpp) *(this->m_iterpp) = newp; // Iterate on new item
this->debugTreeChange("-addHereOut: ", __LINE__, true);
}
void AstNode::setOp1p(AstNode* newp) {
UASSERT(newp,"Null item passed to setOp1p\n");
UDEBUGONLY(if (m_op1p) this->v3fatalSrc("Adding to non-empty, non-list op1"););
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op1"););
this->debugTreeChange("-setOp1pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp1pNew: ", __LINE__, true);
m_op1p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp1pOut: ", __LINE__, false);
}
void AstNode::setOp2p(AstNode* newp) {
UASSERT(newp,"Null item passed to setOp2p\n");
UDEBUGONLY(if (m_op2p) this->v3fatalSrc("Adding to non-empty, non-list op2"););
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op2"););
this->debugTreeChange("-setOp2pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp2pNew: ", __LINE__, true);
m_op2p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp2pOut: ", __LINE__, false);
}
void AstNode::setOp3p(AstNode* newp) {
UASSERT(newp,"Null item passed to setOp3p\n");
UDEBUGONLY(if (m_op3p) this->v3fatalSrc("Adding to non-empty, non-list op3"););
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op3"););
this->debugTreeChange("-setOp3pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp3pNew: ", __LINE__, true);
m_op3p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp3pOut: ", __LINE__, false);
}
void AstNode::setOp4p(AstNode* newp) {
UASSERT(newp,"Null item passed to setOp4p\n");
UDEBUGONLY(if (m_op4p) this->v3fatalSrc("Adding to non-empty, non-list op4"););
UDEBUGONLY(if (newp->m_backp) newp->v3fatalSrc("Adding already linked node"););
UDEBUGONLY(if (newp->m_nextp) newp->v3fatalSrc("Adding list to non-list op4"););
this->debugTreeChange("-setOp4pThs: ", __LINE__, false);
newp->debugTreeChange("-setOp4pNew: ", __LINE__, true);
m_op4p = newp;
newp->editCountInc();
newp->m_backp = this;
this->debugTreeChange("-setOp4pOut: ", __LINE__, false);
}
void AstNode::addOp1p(AstNode* newp) {
UASSERT(newp,"Null item passed to addOp1p\n");
if (!m_op1p) { op1p(newp); }
else { m_op1p->addNext(newp); }
}
void AstNode::addOp2p(AstNode* newp) {
UASSERT(newp,"Null item passed to addOp2p\n");
if (!m_op2p) { op2p(newp); }
else { m_op2p->addNext(newp); }
}
void AstNode::addOp3p(AstNode* newp) {
UASSERT(newp,"Null item passed to addOp3p\n");
if (!m_op3p) { op3p(newp); }
else { m_op3p->addNext(newp); }
}
void AstNode::addOp4p(AstNode* newp) {
UASSERT(newp,"Null item passed to addOp4p\n");
if (!m_op4p) { op4p(newp); }
else { m_op4p->addNext(newp); }
}
void AstNode::replaceWith(AstNode* newp) {
// Replace oldp with this
// Unlike a unlink/relink, children are changed to point to the new node.
AstNRelinker repHandle;
this->unlinkFrBack(&repHandle);
repHandle.relink(newp);
}
void AstNRelinker::dump(ostream& str) {
str<<" BK="<<(uint32_t*)m_backp;
str<<" ITER="<<(uint32_t*)m_iterpp;
str<<" CHG="<<(m_chg==RELINK_NEXT?"[NEXT] ":"");
str<<(m_chg==RELINK_OP1?"[OP1] ":"");
str<<(m_chg==RELINK_OP2?"[OP2] ":"");
str<<(m_chg==RELINK_OP3?"[OP3] ":"");
str<<(m_chg==RELINK_OP4?"[OP4] ":"");
}
AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
this->debugTreeChange("-unlinkWNextThs: ", __LINE__, true);
AstNode* oldp = this;
UASSERT(oldp->m_backp,"Node has no back, already unlinked?\n");
oldp->editCountInc();
AstNode* backp = oldp->m_backp;
if (linkerp) {
linkerp->m_oldp = oldp;
linkerp->m_backp = backp;
linkerp->m_iterpp = oldp->m_iterpp;
if (backp->m_nextp == oldp) linkerp->m_chg = AstNRelinker::RELINK_NEXT;
else if (backp->m_op1p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP1;
else if (backp->m_op2p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP2;
else if (backp->m_op3p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP3;
else if (backp->m_op4p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP4;
else oldp->v3fatalSrc("Unlink of node with back not pointing to it.");
}
if (backp->m_nextp== oldp) {
backp->m_nextp= NULL;
// Old list gets truncated
// New list becomes a list upon itself
// Most common case is unlinking a entire operand tree
// (else we'd probably call unlinkFrBack without next)
// We may be in the middle of a list; we have no way to find head or tail!
AstNode* oldtailp = oldp;
while (oldtailp->m_nextp) oldtailp=oldtailp->m_nextp;
// Create new head/tail of old list
AstNode* oldheadp = oldtailp->m_headtailp;
oldheadp->m_headtailp = oldp->m_backp;
oldheadp->m_headtailp->m_headtailp = oldheadp;
// Create new head/tail of extracted list
oldp->m_headtailp = oldtailp;
oldp->m_headtailp->m_headtailp = oldp;
}
else if (backp->m_op1p == oldp) backp->m_op1p = NULL;
else if (backp->m_op2p == oldp) backp->m_op2p = NULL;
else if (backp->m_op3p == oldp) backp->m_op3p = NULL;
else if (backp->m_op4p == oldp) backp->m_op4p = NULL;
else this->v3fatalSrc("Unlink of node with back not pointing to it.");
// Relink
oldp->m_backp = NULL;
// Iterator fixup
if (oldp->m_iterpp) *(oldp->m_iterpp) = NULL;
oldp->m_iterpp = NULL;
oldp->debugTreeChange("-unlinkWNextOut: ", __LINE__, true);
return oldp;
}
AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
this->debugTreeChange("-unlinkFrBkThs: ", __LINE__, true);
AstNode* oldp = this;
UASSERT(oldp->m_backp,"Node has no back, already unlinked?\n");
oldp->editCountInc();
AstNode* backp = oldp->m_backp;
if (linkerp) {
linkerp->m_oldp = oldp;
linkerp->m_backp = backp;
linkerp->m_iterpp = oldp->m_iterpp;
if (backp->m_nextp == oldp) linkerp->m_chg = AstNRelinker::RELINK_NEXT;
else if (backp->m_op1p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP1;
else if (backp->m_op2p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP2;
else if (backp->m_op3p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP3;
else if (backp->m_op4p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP4;
else this->v3fatalSrc("Unlink of node with back not pointing to it.");
}
if (backp->m_nextp== oldp) {
// This node gets removed from middle (or tail) of list
// Not head, since then oldp wouldn't be a next of backp...
backp->m_nextp= oldp->m_nextp;
if (backp->m_nextp) backp->m_nextp->m_backp = backp;
// If it was a tail, back becomes new tail
if (oldp->m_headtailp) {
backp->m_headtailp = oldp->m_headtailp;
backp->m_headtailp->m_headtailp = backp;
}
}
else {
if (backp->m_op1p == oldp) backp->m_op1p = oldp->m_nextp;
else if (backp->m_op2p == oldp) backp->m_op2p = oldp->m_nextp;
else if (backp->m_op3p == oldp) backp->m_op3p = oldp->m_nextp;
else if (backp->m_op4p == oldp) backp->m_op4p = oldp->m_nextp;
else this->v3fatalSrc("Unlink of node with back not pointing to it.");
if (oldp->m_nextp) {
AstNode* newheadp = oldp->m_nextp;
newheadp->m_backp = backp;
newheadp->m_headtailp = oldp->m_headtailp;
newheadp->m_headtailp->m_headtailp = newheadp;
}
}
// Iterator fixup
if (oldp->m_iterpp) *(oldp->m_iterpp) = oldp->m_nextp;
// Relink
oldp->m_nextp = NULL;
oldp->m_backp = NULL;
oldp->m_headtailp = this;
oldp->m_iterpp = NULL;
oldp->debugTreeChange("-unlinkFrBkOut: ", __LINE__, true);
return oldp;
}
void AstNode::relink(AstNRelinker* linkerp) {
if (debug()>8) { UINFO(0," EDIT: relink: "); dumpPtrs(); }
AstNode* newp = this;
UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker\n");
UASSERT(newp->backp()==NULL, "New node already linked?\n");
newp->editCountInc();
if (debug()>8) { linkerp->dump(cout); cout<<endl; }
AstNode* backp = linkerp->m_backp;
this->debugTreeChange("-relinkNew: ", __LINE__, true);
backp->debugTreeChange("-relinkTre: ", __LINE__, true);
switch (linkerp->m_chg) {
case AstNRelinker::RELINK_NEXT: backp->addNextHere(newp); break;
case AstNRelinker::RELINK_OP1: relinkOneLink(backp->m_op1p /*ref*/, newp); break;
case AstNRelinker::RELINK_OP2: relinkOneLink(backp->m_op2p /*ref*/, newp); break;
case AstNRelinker::RELINK_OP3: relinkOneLink(backp->m_op3p /*ref*/, newp); break;
case AstNRelinker::RELINK_OP4: relinkOneLink(backp->m_op4p /*ref*/, newp); break;
default:
this->v3fatalSrc("Relink of node without any link to change.");
break;
}
// Relink
newp->m_backp = backp;
linkerp->m_backp = NULL;
// Iterator fixup
if (linkerp->m_iterpp) {
// If we're iterating over a next() link, we need to follow links off the
// NEW node. Thus we pass iteration information via a pointer in the node.
// This adds a unfortunate 4 bytes to every AstNode, but is faster then passing
// across every function.
// If anyone has a cleaner way, I'd be grateful.
*(linkerp->m_iterpp) = newp;
newp->m_iterpp = linkerp->m_iterpp;
}
// Empty the linker so not used twice accidentally
linkerp->m_backp = NULL;
this->debugTreeChange("-relinkOut: ", __LINE__, true);
}
void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set to newp
AstNode* newp) {
if (pointpr) {
// We know there will be at least two elements when we are done,
// (newp & the old list).
// We *ALLOW* the new node to have its own next list.
// Likewise there may be a old list.
// Insert the whole old list following the new node's list.
// Thus a unlink without next, followed by relink, gives the same list.
AstNode* newlistlastp=newp->m_headtailp;
if (newlistlastp->m_nextp && newlistlastp!=newp) newp->v3fatalSrc("Headtailp tail isn't at the tail");
AstNode* oldlistlastp = pointpr->m_headtailp;
if (oldlistlastp->m_nextp && oldlistlastp!=pointpr) newp->v3fatalSrc("Old headtailp tail isn't at the tail");
// Next links
newlistlastp->m_nextp = pointpr;
pointpr->m_backp = newlistlastp;
// Head/tail
pointpr->m_headtailp = NULL; // Old head
newlistlastp->m_headtailp = NULL; // Old tail
newp->m_headtailp = oldlistlastp; // Head points to tail
oldlistlastp->m_headtailp = newp; // Tail points to head
}
pointpr = newp;
}
//======================================================================
// Clone
AstNode* AstNode::cloneTreeIter() {
if (!this) return NULL;
AstNode* newp = this->clone();
newp->op1p(this->m_op1p->cloneTreeIterList());
newp->op2p(this->m_op2p->cloneTreeIterList());
newp->op3p(this->m_op3p->cloneTreeIterList());
newp->op4p(this->m_op4p->cloneTreeIterList());
newp->m_iterpp = NULL;
newp->clonep(this); // Save pointers to/from both to simplify relinking.
this->clonep(newp); // Save pointers to/from both to simplify relinking.
return newp;
}
AstNode* AstNode::cloneTreeIterList() {
// Clone list of nodes, set m_headtailp
if (!this) return NULL;
AstNode* newheadp = NULL;
AstNode* newtailp = NULL;
for (AstNode* oldp = this; oldp; oldp=oldp->m_nextp) {
AstNode* newp = oldp->cloneTreeIter();
newp->m_headtailp = NULL;
newp->m_backp = newtailp;
if (newtailp) newtailp->m_nextp = newp;
if (!newheadp) newheadp = newp;
newtailp = newp;
}
newheadp->m_headtailp = newtailp;
newtailp->m_headtailp = newheadp;
return newheadp;
}
AstNode* AstNode::cloneTree(bool cloneNextLink) {
if (!this) return NULL;
this->debugTreeChange("-cloneThs: ", __LINE__, cloneNextLink);
cloneClearTree();
AstNode* newp;
if (cloneNextLink && this->m_nextp) {
newp = cloneTreeIterList();
} else {
newp = cloneTreeIter();
newp->m_nextp = NULL;
newp->m_headtailp = newp;
}
newp->m_backp = NULL;
newp->cloneRelinkTree();
newp->debugTreeChange("-cloneOut: ", __LINE__, true);
return newp;
}
//======================================================================
// Delete
void AstNode::deleteNode() {
if (!this) return;
UASSERT(m_backp==NULL,"Delete called on node with backlink still set\n");
editCountInc();
// Change links of old node so we coredump if used
this->m_nextp = (AstNode*)1;
this->m_backp = (AstNode*)1;
this->m_headtailp = (AstNode*)1;
this->m_op1p = (AstNode*)1;
this->m_op2p = (AstNode*)1;
this->m_op3p = (AstNode*)1;
this->m_op4p = (AstNode*)1;
#if !defined(VL_DEBUG) || defined(VL_LEAK_CHECKS)
delete this; // Leak massively, so each pointer is unique and we can debug easier
#endif
}
AstNode::~AstNode() {
}
void AstNode::deleteTreeIter() {
if (!this) return;
// MUST be depth first!
this->m_op1p->deleteTreeIter();
this->m_op2p->deleteTreeIter();
this->m_op3p->deleteTreeIter();
this->m_op4p->deleteTreeIter();
this->m_nextp->deleteTreeIter();
this->m_backp = NULL;
deleteNode();
}
void AstNode::deleteTree() {
// deleteTree always deletes the next link, because you must have called
// unlinkFromBack or unlinkFromBackWithNext as appropriate before calling this.
if (!this) return;
UASSERT(m_backp==NULL,"Delete called on node with backlink still set\n");
this->debugTreeChange("-delete: ", __LINE__, true);
// MUST be depth first!
deleteTreeIter();
}
//======================================================================
// Iterators
void AstNode::iterateChildren(AstNVisitor& v, AstNUser* vup) {
if (!this) return;
m_op1p->iterateAndNext(v, vup);
m_op2p->iterateAndNext(v, vup);
m_op3p->iterateAndNext(v, vup);
m_op4p->iterateAndNext(v, vup);
}
void AstNode::iterateListBackwards(AstNVisitor& v, AstNUser* vup) {
if (!this) return;
AstNode* nodep=this;
while (nodep->m_nextp) nodep=nodep->m_nextp;
while (nodep) {
// Edits not supported: nodep->m_iterpp = &nodep;
nodep->accept(v, vup);
if (nodep->backp()->m_nextp == nodep) nodep=nodep->backp();
else nodep = NULL; // else: backp points up the tree.
}
}
void AstNode::iterateChildrenBackwards(AstNVisitor& v, AstNUser* vup) {
if (!this) return;
this->op1p()->iterateListBackwards(v,vup);
this->op2p()->iterateListBackwards(v,vup);
this->op3p()->iterateListBackwards(v,vup);
this->op4p()->iterateListBackwards(v,vup);
}
void AstNode::iterateAndNext(AstNVisitor& v, AstNUser* vup) {
// IMPORTANT: If you replace a node that's the target of this iterator,
// then the NEW node will be iterated on next, it isn't skipped!
// if (!this) return; // Part of for()
for (AstNode* nodep=this; nodep;) {
AstNode* niterp = nodep;
niterp->m_iterpp = &niterp;
niterp->accept(v, vup);
// accept may do a replaceNode and change niterp on us...
if (!niterp) return;
niterp->m_iterpp = NULL;
if (niterp!=nodep) { // Edited it
nodep = niterp;
} else { // Same node, just loop
nodep = niterp->m_nextp;
}
}
}
void AstNode::iterateAndNextIgnoreEdit(AstNVisitor& v, AstNUser* vup) {
// Keep following the current list even if edits change it
if (!this) return;
for (AstNode* nodep=this; nodep; ) {
AstNode* nnextp = nodep->m_nextp;
nodep->accept(v, vup);
nodep = nnextp;
}
}
//======================================================================
void AstNode::cloneRelinkTree() {
if (!this) return;
this->cloneRelink();
m_op1p->cloneRelinkTree();
m_op2p->cloneRelinkTree();
m_op3p->cloneRelinkTree();
m_op4p->cloneRelinkTree();
m_nextp->cloneRelinkTree(); // Tail recursion
}
//======================================================================
// Comparison
bool AstNode::sameTree(AstNode* node2p) {
return sameTreeIter(node2p, true);
}
bool AstNode::sameTreeIter(AstNode* node2p, bool ignNext) {
// Return true if the two trees are identical
if (this==NULL && node2p==NULL) return true;
if (this==NULL || node2p==NULL) return false;
if (this->type() != node2p->type()
|| this->width() != node2p->width()
|| this->isSigned() != node2p->isSigned()
|| !this->same(node2p)) {
return false;
}
return (this->op1p()->sameTreeIter(node2p->op1p(),false)
&& this->op2p()->sameTreeIter(node2p->op2p(),false)
&& this->op3p()->sameTreeIter(node2p->op3p(),false)
&& this->op4p()->sameTreeIter(node2p->op4p(),false)
&& (ignNext || this->nextp()->sameTreeIter(node2p->nextp(),false))
);
}
//======================================================================
// Static utilities
ostream& operator<<(ostream& os, V3Hash rhs) {
return os<<hex<<setw(2)<<setfill('0')<<rhs.depth()
<<"_"<<setw(6)<<setfill('0')<<rhs.hshval();
}
V3Hash::V3Hash(const string& name) {
uint32_t val = 0;
for (const char* c=name.c_str(); *c; c++) {
val = val*31 + *c;
}
setBoth(1,val);
}
//======================================================================
// Debugging
void AstNode::checkTreeIter(AstNode* backp) {
if (backp != this->backp()) {
this->v3fatalSrc("Back node inconsistent");
}
if (op1p()) op1p()->checkTreeIterList(this);
if (op2p()) op2p()->checkTreeIterList(this);
if (op3p()) op3p()->checkTreeIterList(this);
if (op4p()) op4p()->checkTreeIterList(this);
}
void AstNode::checkTreeIterList(AstNode* backp) {
// Check a (possible) list of nodes, this is always the head of the list
if (!this) v3fatalSrc("Null nodep");
AstNode* headp = this;
AstNode* tailp = this;
for (AstNode* nodep=headp; nodep; nodep=nodep->nextp()) {
nodep->checkTreeIter(backp);
if (headp!=this && nextp()) this->v3fatalSrc("Headtailp should be null in middle of lists");
tailp=nodep;
backp=nodep;
}
if (headp->m_headtailp != tailp) headp->v3fatalSrc("Tail in headtailp is inconsistent");
if (tailp->m_headtailp != headp) tailp->v3fatalSrc("Head in headtailp is inconsistent");
}
void AstNode::checkTree() {
if (!this) return;
if (!debug()) return;
if (this->backp()) {
// Linked tree- check only the passed node
this->checkTreeIter(this->backp());
} else {
this->checkTreeIterList(this->backp());
}
}
void AstNode::dumpPtrs(ostream& os) {
os<<"This="<<typeName()<<" "<<(void*)this;
os<<" back="<<(void*)backp();
if (nextp()) os<<" next="<<(void*)nextp();
if (m_headtailp==this) os<<" headtail=this";
else os<<" headtail="<<(void*)m_headtailp;
if (op1p()) os<<" op1p="<<(void*)op1p();
if (op2p()) os<<" op2p="<<(void*)op2p();
if (op3p()) os<<" op3p="<<(void*)op3p();
if (op4p()) os<<" op4p="<<(void*)op4p();
if (userp()) os<<" user="<<(void*)userp();
if (m_iterpp) {
os<<" iterpp="<<(void*)m_iterpp;
os<<"*="<<(void*)*m_iterpp;
}
os<<endl;
}
void AstNode::dumpTree(ostream& os, const string& indent, int maxDepth) {
if (!this) return;
os<<indent<<" "<<this<<endl;
if (debug()>8) { os<<indent<<" "; dumpPtrs(os); }
if (maxDepth==1) {
if (op1p()||op2p()||op3p()||op4p()) { os<<indent<<"1: ...(maxDepth)"<<endl; }
} else {
for (AstNode* nodep=op1p(); nodep; nodep=nodep->nextp()) { nodep->dumpTree(os,indent+"1:",maxDepth-1); }
for (AstNode* nodep=op2p(); nodep; nodep=nodep->nextp()) { nodep->dumpTree(os,indent+"2:",maxDepth-1); }
for (AstNode* nodep=op3p(); nodep; nodep=nodep->nextp()) { nodep->dumpTree(os,indent+"3:",maxDepth-1); }
for (AstNode* nodep=op4p(); nodep; nodep=nodep->nextp()) { nodep->dumpTree(os,indent+"4:",maxDepth-1); }
}
}
void AstNode::dumpTreeAndNext(ostream& os, const string& indent, int maxDepth) {
if (!this) return;
for (AstNode* nodep=this; nodep; nodep=nodep->nextp()) {
nodep->dumpTree(os, indent, maxDepth);
}
}
void AstNode::dumpTreeFile(const string& filename, bool append) {
if (v3Global.opt.dumpTree()) {
{ // Write log & close
const auto_ptr<ofstream> logsp (V3File::new_ofstream(filename, append));
if (logsp->fail()) v3fatalSrc("Can't write "<<filename);
*logsp<<"Tree Dump from <e"<<dec<<editCountLast()<<">";
*logsp<<" to <e"<<dec<<editCountGbl()<<">"<<endl;
if (editCountGbl()==editCountLast() && 0) { // Off, as messes up tree diffing
*logsp<<endl;
*logsp<<"No changes since last dump!\n";
} else {
dumpTree(*logsp);
}
}
// Error check
checkTree();
// Broken isn't part of check tree because it can munge iterp's
// set by other steps if it is called in the middle of other operations
if (AstNetlist* netp=this->castNetlist()) V3Broken::brokenAll(netp);
// Next dump can indicate start from here
editCountSetLast();
}
}
void AstNode::v3errorEnd(ostringstream& str) {
if (this && m_fileline) {
ostringstream nsstr;
nsstr<<str.str();
if (debug()) {
nsstr<<endl;
nsstr<<"-node: "<<this<<endl;
}
m_fileline->v3errorEnd(nsstr);
} else {
V3Error::v3errorEnd(str);
}
}
//######################################################################
void AstNVisitor::doDeletes() {
for (vector<AstNode*>::iterator it = m_deleteps.begin(); it != m_deleteps.end(); ++it) {
(*it)->deleteTree();
}
m_deleteps.clear();
}

982
src/V3Ast.h Normal file
View File

@ -0,0 +1,982 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Ast node structure
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3AST_H_
#define _V3AST_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Number.h"
#include <vector>
#include "V3Ast__gen_classes.h" // From ./astgen
// Things like:
// class V3AstNode;
//######################################################################
class AstType {
public:
#include "V3Ast__gen_types.h" // From ./astgen
// Above include has:
// enum en {...};
// const char* ascii() const {...};
enum en m_e;
inline AstType () {};
inline AstType (en _e) : m_e(_e) {};
explicit inline AstType (int _e) : m_e(static_cast<en>(_e)) {};
operator en () const { return m_e; };
};
inline bool operator== (AstType lhs, AstType rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstType lhs, AstType::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstType::en lhs, AstType rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, AstType rhs) { return os<<rhs.ascii(); }
//######################################################################
class AstPragmaType {
public:
enum en {
COVERAGE_BLOCK_OFF,
INLINE_MODULE,
NO_INLINE_MODULE,
PUBLIC_MODULE,
PUBLIC_TASK
};
enum en m_e;
inline AstPragmaType () {};
inline AstPragmaType (en _e) : m_e(_e) {};
explicit inline AstPragmaType (int _e) : m_e(static_cast<en>(_e)) {};
operator en () const { return m_e; };
};
inline bool operator== (AstPragmaType lhs, AstPragmaType rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstPragmaType lhs, AstPragmaType::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstPragmaType::en lhs, AstPragmaType rhs) { return (lhs == rhs.m_e); }
//######################################################################
class AstCFuncType {
public:
enum en {
NORMAL,
TRACE_INIT,
TRACE_FULL,
TRACE_CHANGE
};
enum en m_e;
inline AstCFuncType () {};
inline AstCFuncType (en _e) : m_e(_e) {};
explicit inline AstCFuncType (int _e) : m_e(static_cast<en>(_e)) {};
operator en () const { return m_e; };
};
inline bool operator== (AstCFuncType lhs, AstCFuncType rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstCFuncType lhs, AstCFuncType::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstCFuncType::en lhs, AstCFuncType rhs) { return (lhs == rhs.m_e); }
//######################################################################
class AstEdgeType {
public:
// REMEMBER to edit the strings below too
enum en {
// These must be in general -> most specific order, as we sort by it in AstSenTree::sortSenses()
ILLEGAL,
// Involving a variable
ANYEDGE, // Default for sensitivities; rip them out
BOTHEDGE, // POSEDGE | NEGEDGE
POSEDGE,
NEGEDGE,
HIGHEDGE, // Is high now (latches)
LOWEDGE, // Is low now (latches)
// Not involving anything
COMBO, // Sensitive to all combo inputs to this block
INITIAL, // User initial statements
SETTLE, // Like combo but for initial wire resolutions after initial statement
NEVER // Never occurs (optimized away)
};
enum en m_e;
bool clockedStmt() const {
static const bool clocked[] = {
false, false, true, true, true, true, true,
false, false, false
};
return clocked[m_e];
}
AstEdgeType invert() const {
switch (m_e) {
case ANYEDGE: return ANYEDGE;
case BOTHEDGE: return BOTHEDGE;
case POSEDGE: return NEGEDGE;
case NEGEDGE: return POSEDGE;
case HIGHEDGE: return LOWEDGE;
case LOWEDGE: return HIGHEDGE;
default: UASSERT_STATIC(0,"Inverting bad edgeType()");
};
return AstEdgeType::ILLEGAL;
}
const char* ascii() const {
static const char* names[] = {
"%E-edge", "ANY", "BOTH", "POS", "NEG", "HIGH", "LOW",
"COMBO","INITIAL","SETTLE","NEVER"
};
return names[m_e];
};
const char* verilogKwd() const {
static const char* names[] = {
"%E-edge", "", "[both]", "posedge", "negedge", "[high]","[low]",
"/*AS*/","[initial]","[settle]","[never]"
};
return names[m_e];
};
inline AstEdgeType () {};
inline AstEdgeType (en _e) : m_e(_e) {};
explicit inline AstEdgeType (int _e) : m_e(static_cast<en>(_e)) {};
operator en () const { return m_e; };
};
inline bool operator== (AstEdgeType lhs, AstEdgeType rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstEdgeType lhs, AstEdgeType::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstEdgeType::en lhs, AstEdgeType rhs) { return (lhs == rhs.m_e); }
//######################################################################
class AstAttrType {
public:
enum en {
BITS,
RANGE_LSB,
ARRAY_LSB,
SCOPE_TEXT
};
enum en m_e;
inline AstAttrType () {};
inline AstAttrType (en _e) : m_e(_e) {};
explicit inline AstAttrType (int _e) : m_e(static_cast<en>(_e)) {};
operator en () const { return m_e; };
};
inline bool operator== (AstAttrType lhs, AstAttrType rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstAttrType lhs, AstAttrType::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstAttrType::en lhs, AstAttrType rhs) { return (lhs == rhs.m_e); }
//######################################################################
class AstVarType {
public:
enum en {
UNKNOWN,
GPARAM,
LPARAM,
GENVAR,
INTEGER,
INPUT,
OUTPUT,
INOUT,
SUPPLY0,
SUPPLY1,
WIRE,
IMPLICIT,
REG,
TRIWIRE,
BLOCKTEMP,
MODULETEMP,
STMTTEMP
};
enum en m_e;
inline AstVarType () {};
inline AstVarType (en _e) : m_e(_e) {};
explicit inline AstVarType (int _e) : m_e(static_cast<en>(_e)) {};
operator en () const { return m_e; };
const char* ascii() const {
static const char* names[] = {
"?","GPARAM","LPARAM","GENVAR",
"INTEGER","INPUT","OUTPUT","INOUT",
"SUPPLY0","SUPPLY1","WIRE","IMPLICIT","REG","TRIWIRE",
"BLOCKTEMP","MODULETEMP","STMTTEMP"};
return names[m_e];};
};
inline bool operator== (AstVarType lhs, AstVarType rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstVarType lhs, AstVarType::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstVarType::en lhs, AstVarType rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, AstVarType rhs) { return os<<rhs.ascii(); }
//######################################################################
class AstBranchPred {
public:
enum en {
UNKNOWN=0,
LIKELY,
UNLIKELY,
_ENUM_END
};
enum en m_e;
// CONSTRUCTOR - note defaults to *UNKNOWN*
inline AstBranchPred () : m_e(UNKNOWN) {};
inline AstBranchPred (en _e) : m_e(_e) {};
explicit inline AstBranchPred (int _e) : m_e(static_cast<en>(_e)) {};
operator en () const { return m_e; };
AstBranchPred invert() const {
if (m_e==UNLIKELY) return LIKELY;
else if (m_e==LIKELY) return UNLIKELY;
else return m_e;
}
const char* ascii() const {
static const char* names[] = {
"","VL_LIKELY","VL_UNLIKELY"};
return names[m_e];};
};
inline bool operator== (AstBranchPred lhs, AstBranchPred rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstBranchPred lhs, AstBranchPred::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (AstBranchPred::en lhs, AstBranchPred rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, AstBranchPred rhs) { return os<<rhs.ascii(); }
//######################################################################
// AstNUser - Generic pointer base class for AST User nodes.
// - Also used to allow parameter passing up/down iterate calls
class WidthVP;
class LinkVP;
class OrderBlockNU;
class OrderVarNU;
class V3GraphVertex;
class V3SymTable;
struct AstNUser {
AstNUser* p() { return this; } // So can take address of temporary: iterate(...,AstNUser(args).p())
// Casters
WidthVP* c() { return ((WidthVP*)this); }
LinkVP* castLinkVP() { return ((LinkVP*)this); }
V3SymTable* castSymTable() { return ((V3SymTable*)this); }
AstNode* castNode() { return ((AstNode*)this); }
OrderBlockNU* castOrderBlock() { return ((OrderBlockNU*)this); }
OrderVarNU* castOrderVar() { return ((OrderVarNU*)this); }
V3GraphVertex* castGraphVertex() { return ((V3GraphVertex*)this); }
inline int castInt() {
union { AstNUser* up; int ui; } u;
u.up = this;
return u.ui;
}
static inline AstNUser* fromInt (int i) {
union { AstNUser* up; int ui; } u;
u.up=0; u.ui=i;
return u.up;
}
};
//######################################################################
// AstNVisitor -- Allows new functions to be called on each node
// type without changing the base classes. See "Modern C++ Design".
class AstNVisitor {
private:
vector<AstNode*> m_deleteps; // Nodes to delete when we are finished
protected:
friend class AstNode;
public:
// Cleaning
void pushDeletep(AstNode* nodep) {
m_deleteps.push_back(nodep);
}
void doDeletes();
public:
virtual ~AstNVisitor() {
doDeletes();
}
#include "V3Ast__gen_visitor.h" // From ./astgen
// Things like:
// virtual void visit(type*) = 0;
};
//######################################################################
// AstNRelinker -- Holds the state of a unlink so a new node can be
// added at the same point.
class AstNRelinker {
protected:
friend class AstNode;
enum RelinkWhatEn {
RELINK_BAD, RELINK_NEXT, RELINK_OP1, RELINK_OP2, RELINK_OP3, RELINK_OP4
};
AstNode* m_oldp; // The old node that was linked to this point in the tree
AstNode* m_backp;
RelinkWhatEn m_chg;
AstNode** m_iterpp;
public:
AstNRelinker() { m_backp=NULL; m_chg=RELINK_BAD; m_iterpp=NULL;}
void relink(AstNode* newp);
AstNode* oldp() const { return m_oldp; }
void dump(ostream& str=cout);
};
inline ostream& operator<<(ostream& os, AstNRelinker& rhs) { rhs.dump(os); return os;}
//######################################################################
// V3Hash -- Node hashing for V3Combine
class V3Hash {
// A hash of a tree of nodes, consisting of 8 bits with the number of nodes in the hash
// and 24 bit value hash of relevant information about the node.
// A value of 0 is illegal
uint32_t m_both;
static const uint32_t M24 = ((1<<24)-1);
void setBoth(uint32_t depth, uint32_t hshval) {
if (depth==0) depth=1; if (depth>255) depth=255;
m_both = (depth<<24) | (hshval & M24);
}
public:
// METHODS
bool isIllegal() const { return m_both==0; }
uint32_t fullValue() const { return m_both; }
uint32_t depth() const { return (m_both >> 24) & 255; }
uint32_t hshval() const { return m_both & M24; }
// OPERATORS
inline bool operator== (const V3Hash& rh) const { return m_both==rh.m_both; };
inline bool operator< (const V3Hash& rh) const { return m_both<rh.m_both; };
// CREATORS
class Illegal {}; // for creator type-overload selection
class FullValue {}; // for creator type-overload selection
V3Hash(Illegal) { m_both=0; }
// Saving and restoring inside a userp
V3Hash(AstNUser* up) { m_both=up->castInt(); }
V3Hash operator+= (const V3Hash& rh) {
setBoth(depth()+rh.depth(), (hshval()*31+rh.hshval()));
return *this; };
// Creating from raw data (sameHash functions)
V3Hash() { setBoth(1,0); }
V3Hash(uint32_t val) { setBoth(1,val); }
V3Hash(void* vp) {
// It's just a hash, so we can shove a 64 bit pointer into a 32 bit bucket
// On 32 bit systems, lower is always 0, but who cares?
union { void* up; struct {uint32_t upper; uint32_t lower;} l;} u;
u.l.upper=0; u.l.lower=0; u.up=vp;
setBoth(1,u.l.upper^u.l.lower);
}
V3Hash(const string& name);
V3Hash(V3Hash lh, V3Hash rh) {
setBoth(1,lh.hshval()*31+rh.hshval());
}
};
ostream& operator<<(ostream& os, V3Hash rhs);
//######################################################################
// AstNode -- Base type of all Ast types
class AstNode {
private:
AstNode* m_nextp; // Next peer in the parent's list
AstNode* m_backp; // Node that points to this one (via next/op1/op2/...)
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
AstNode* m_op1p; // Generic pointer 1
AstNode* m_op2p; // Generic pointer 2
AstNode* m_op3p; // Generic pointer 3
AstNode* m_op4p; // Generic pointer 4
AstNode** m_iterpp; // Pointer to node iterating on, change it if we replace this node.
AstNode* m_clonep; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY)
int m_cloneCnt; // Mark of when userp was set
static int s_cloneCntGbl; // Count of which userp is set
FileLine* m_fileline; // Where it was declared
uint64_t m_editCount; // When it was last edited
static uint64_t s_editCntGbl;// Global edit counter
static uint64_t s_editCntLast;// Global edit counter, last value for printing * near node #s
// Attributes
bool m_signed; // Node is signed
int m_width; // Bit width of operation
int m_widthMin; // If unsized, bitwidth of minimum implementation
AstNUser* m_userp; // Pointer to any information the user iteration routine wants
int m_userCnt; // Mark of when userp was set
static int s_userCntGbl; // Count of which userp is set
AstNUser* m_user2p; // Pointer to any information the user iteration routine wants
int m_user2Cnt; // Mark of when userp was set
static int s_user2CntGbl; // Count of which userp is set
AstNUser* m_user3p; // Pointer to any information the user iteration routine wants
int m_user3Cnt; // Mark of when userp was set
static int s_user3CntGbl; // Count of which userp is set
AstNUser* m_user4p; // Pointer to any information the user iteration routine wants
int m_user4Cnt; // Mark of when userp was set
static int s_user4CntGbl; // Count of which userp is set
AstNUser* m_user5p; // Pointer to any information the user iteration routine wants
int m_user5Cnt; // Mark of when userp was set
static int s_user5CntGbl; // Count of which userp is set
// METHODS
void op1p(AstNode* nodep) { m_op1p = nodep; if (nodep) nodep->m_backp = this; }
void op2p(AstNode* nodep) { m_op2p = nodep; if (nodep) nodep->m_backp = this; }
void op3p(AstNode* nodep) { m_op3p = nodep; if (nodep) nodep->m_backp = this; }
void op4p(AstNode* nodep) { m_op4p = nodep; if (nodep) nodep->m_backp = this; }
void init(); // initialize value of AstNode
void iterateListBackwards(AstNVisitor& v, AstNUser* vup=NULL);
AstNode* cloneTreeIter();
AstNode* cloneTreeIterList();
void checkTreeIter(AstNode* backp);
void checkTreeIterList(AstNode* backp);
bool sameTreeIter(AstNode* node2p, bool ignNext);
void deleteTreeIter();
void deleteNode();
static void relinkOneLink(AstNode*& pointpr, AstNode* newp);
void debugTreeChange(const char* prefix, int lineno, bool next);
protected:
// CONSTUCTORS
AstNode() {init(); }
AstNode(FileLine* fileline) {init(); m_fileline = fileline; }
virtual AstNode* clone() = 0; // Generally, cloneTree/cloneNode is what you want
virtual void cloneRelink() {}
void cloneRelinkTree();
// METHODS
void setOp1p(AstNode* newp); // Set non-list-type op1 to non-list element
void setOp2p(AstNode* newp); // Set non-list-type op2 to non-list element
void setOp3p(AstNode* newp); // Set non-list-type op3 to non-list element
void setOp4p(AstNode* newp); // Set non-list-type op4 to non-list element
void setNOp1p(AstNode* newp) { if (newp) setOp1p(newp); }
void setNOp2p(AstNode* newp) { if (newp) setOp2p(newp); }
void setNOp3p(AstNode* newp) { if (newp) setOp3p(newp); }
void setNOp4p(AstNode* newp) { if (newp) setOp4p(newp); }
void addOp1p(AstNode* newp); // Append newp to end of op1
void addOp2p(AstNode* newp); // Append newp to end of op2
void addOp3p(AstNode* newp); // Append newp to end of op3
void addOp4p(AstNode* newp); // Append newp to end of op4
void addNOp1p(AstNode* newp) { if (newp) addOp1p(newp); }
void addNOp2p(AstNode* newp) { if (newp) addOp2p(newp); }
void addNOp3p(AstNode* newp) { if (newp) addOp3p(newp); }
void addNOp4p(AstNode* newp) { if (newp) addOp4p(newp); }
void clonep(AstNode* nodep) { m_clonep=nodep; m_cloneCnt=s_cloneCntGbl; }
static void cloneClearTree() { s_cloneCntGbl++; UASSERT_STATIC(s_cloneCntGbl,"Rollover"); }
public:
// ACCESSORS
virtual AstType type() const = 0;
const char* typeName() const { return type().ascii(); }
AstNode* nextp() const { return m_nextp; }
AstNode* backp() const { return m_backp; }
AstNode* op1p() const { return m_op1p; }
AstNode* op2p() const { return m_op2p; }
AstNode* op3p() const { return m_op3p; }
AstNode* op4p() const { return m_op4p; }
AstNode* clonep() const { return ((m_cloneCnt==s_cloneCntGbl)?m_clonep:NULL); }
bool brokeExists() const;
// CONSTRUCTORS
virtual ~AstNode();
// CONSTANT ACCESSORS
static int instrCountBranch() { return 4; } ///< Instruction cycles to branch
static int instrCountDiv() { return 10; } ///< Instruction cycles to divide
static int instrCountLd() { return 2; } ///< Instruction cycles to load memory
static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers
static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines
static int instrCountCall() { return instrCountBranch()+10; } ///< Instruction cycles to call subroutine
static int instrCountTime() { return instrCountCall()+5; } ///< Instruction cycles to determine simulation time
// ACCESSORS
virtual string name() const { return ""; }
virtual string verilogKwd() const { return ""; }
string shortName() const; // Name with __PVT__ removed for concatenating scopes
static string prettyName(const string& namein); // Name for printing out to the user
string prettyName() const { return prettyName(name()); }
FileLine* fileline() const { return m_fileline; }
int width() const { return m_width; }
bool width1() const { return width()==1; }
int widthWords() const { return VL_WORDS_I(width()); }
int widthMin() const { return m_widthMin?m_widthMin:m_width; } // If sized, the size, if unsized the min digits to represent it
int widthPow2() const;
int widthInstrs() const { return isWide()?widthWords():1; }
bool widthSized() const { return !m_widthMin || m_widthMin==m_width; }
void width(int width, int sized) { m_width=width; m_widthMin=sized; }
void widthFrom(AstNode* fromp) { if (fromp) { m_width=fromp->m_width; m_widthMin=fromp->m_widthMin; }}
void widthSignedFrom(AstNode* fromp) { widthFrom(fromp); signedFrom(fromp); }
void signedFrom(AstNode* fromp) { if (fromp) { m_signed=fromp->m_signed; }}
void isSigned(bool flag) { m_signed=flag; }
bool isSigned() const { return m_signed; }
bool isQuad() const { return (width()>VL_WORDSIZE && width()<=VL_QUADSIZE); }
bool isWide() const { return (width()>VL_QUADSIZE); }
int user() const { return userp()->castInt(); }
AstNUser* userp() const { return ((m_userCnt==s_userCntGbl)?m_userp:NULL); }
void userp(void* userp) { m_userp=(AstNUser*)(userp); m_userCnt=s_userCntGbl; }
void user(int val) { userp(AstNUser::fromInt(val)); }
static void userClearTree() { s_userCntGbl++; UASSERT_STATIC(s_userCntGbl,"Rollover"); } // Clear userp()'s across the entire tree
int user2() const { return user2p()->castInt(); }
AstNUser* user2p() const { return ((m_user2Cnt==s_user2CntGbl)?m_user2p:NULL); }
void user2p(void* userp) { m_user2p=(AstNUser*)(userp); m_user2Cnt=s_user2CntGbl; }
void user2(int val) { user2p(AstNUser::fromInt(val)); }
static void user2ClearTree() { s_user2CntGbl++; } // Clear userp()'s across the entire tree
int user3() const { return user3p()->castInt(); }
AstNUser* user3p() const { return ((m_user3Cnt==s_user3CntGbl)?m_user3p:NULL); }
void user3p(void* userp) { m_user3p=(AstNUser*)(userp); m_user3Cnt=s_user3CntGbl; }
void user3(int val) { user3p(AstNUser::fromInt(val)); }
static void user3ClearTree() { s_user3CntGbl++; } // Clear userp()'s across the entire tree
int user4() const { return user4p()->castInt(); }
AstNUser* user4p() const { return ((m_user4Cnt==s_user4CntGbl)?m_user4p:NULL); }
void user4p(void* userp) { m_user4p=(AstNUser*)(userp); m_user4Cnt=s_user4CntGbl; }
void user4(int val) { user4p(AstNUser::fromInt(val)); }
static void user4ClearTree() { s_user4CntGbl++; } // Clear userp()'s across the entire tree
int user5() const { return user5p()->castInt(); }
AstNUser* user5p() const { return ((m_user5Cnt==s_user5CntGbl)?m_user5p:NULL); }
void user5p(void* userp) { m_user5p=(AstNUser*)(userp); m_user5Cnt=s_user5CntGbl; }
void user5(int val) { user5p(AstNUser::fromInt(val)); }
static void user5ClearTree() { s_user5CntGbl++; } // Clear userp()'s across the entire tree
uint64_t editCount() const { return m_editCount; }
void editCountInc() { m_editCount = s_editCntGbl++; }
static uint64_t editCountLast() { return s_editCntLast; }
static uint64_t editCountGbl() { return s_editCntGbl; }
static void editCountSetLast() { s_editCntLast = editCountGbl(); }
// ACCESSORS for specific types
// Alas these can't be virtual or they break when passed a NULL
bool isZero();
bool isOne();
bool isNeqZero();
bool isAllOnes();
bool isAllOnesV(); // Verilog width rules apply
// METHODS
AstNode* addNext(AstNode* newp); // Returns this, adds to end of list
AstNode* addNextNull(AstNode* newp); // Returns this, adds to end of list, NULL is OK
void addNextHere(AstNode* newp); // Adds after speced node
void replaceWith(AstNode* newp); // Replace current node in tree with new node
void v3errorEnd(ostringstream& str);
virtual void dump(ostream& str=cout);
AstNode* unlinkFrBack(AstNRelinker* linkerp=NULL); // Unlink this from whoever points to it.
AstNode* unlinkFrBackWithNext(AstNRelinker* linkerp=NULL); // Unlink this from whoever points to it, keep entire next list with unlinked node
void relink(AstNRelinker* linkerp); // Generally use linker->relink() instead
void cloneRelinkNode() { cloneRelink(); }
// METHODS - Iterate on a tree
AstNode* cloneTree(bool cloneNextLink);
bool sameTree(AstNode* node2p); // Does tree of this == node2p?
void deleteTree(); // Always deletes the next link
void checkTree(); // User Interface version
void dumpPtrs(ostream& str=cout);
void dumpTree(ostream& str=cout, const string& indent=" ", int maxDepth=0);
void dumpTreeAndNext(ostream& str=cout, const string& indent=" ", int maxDepth=0);
void dumpTreeFile(const string& filename, bool append=false);
// METHODS - queries
virtual bool isSplittable() const { return true; } // Else a $display, etc, that must be ordered with other displays
virtual bool isGateOptimizable() const { return true; } // Else a AstTime etc that can't be pushed out
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
virtual bool isUnlikely() const { return false; } // Else $stop or similar statement which means an above IF statement is unlikely to be taken
virtual int instrCount() const { return 0; }
virtual V3Hash sameHash() const { return V3Hash(V3Hash::Illegal()); } // Not a node that supports it
virtual bool same(AstNode* otherp) const { return true; }
virtual bool broken() const { return false; }
virtual bool emitWordForm() { return false; }
// INVOKERS
virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) = 0;
void iterate(AstNVisitor& v, AstNUser* vup=NULL) { this->accept(v,vup); } // Does this; excludes following this->next
void iterateAndNext(AstNVisitor& v, AstNUser* vup=NULL);
void iterateAndNextIgnoreEdit(AstNVisitor& v, AstNUser* vup=NULL);
void iterateChildren(AstNVisitor& v, AstNUser* vup=NULL); // Excludes following this->next
void iterateChildrenBackwards(AstNVisitor& v, AstNUser* vup=NULL); // Excludes following this->next
// CONVERSION
AstNode* castNode() { return this; }
#include "V3Ast__gen_interface.h" // From ./astgen
// Things like:
// AstAlways* castAlways();
};
inline ostream& operator<<(ostream& os, AstNode* rhs) { rhs->dump(os); return os;}
inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
//######################################################################
//######################################################################
//=== AstNode* : Derived generic node types
struct AstNodeMath : public AstNode {
// Math -- anything that's part of an expression tree
AstNodeMath(FileLine* fl)
: AstNode(fl) {}
virtual ~AstNodeMath() {}
// METHODS
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
virtual string emitOperator() = 0;
virtual string emitSimpleOperator() { return ""; }
virtual bool cleanOut() = 0; // True if output has extra upper bits zero
};
struct AstNodeTermop : public AstNodeMath {
// Terminal operator -- a operator with no "inputs"
AstNodeTermop(FileLine* fl)
: AstNodeMath(fl) {}
virtual ~AstNodeTermop() {}
};
struct AstNodeUniop : public AstNodeMath {
// Unary math
AstNodeUniop(FileLine* fl, AstNode* lhsp)
: AstNodeMath(fl) {
if (lhsp) widthSignedFrom(lhsp);
setOp1p(lhsp); }
virtual ~AstNodeUniop() {}
AstNode* lhsp() const { return op1p()->castNode(); }
// METHODS
virtual void numberOperate(V3Number& out, const V3Number& lhs) = 0; // Set out to evaluation of a AstConst'ed lhs
virtual bool cleanLhs() = 0;
virtual bool sizeMattersLhs() = 0; // True if output result depends on lhs size
virtual int instrCount() const { return widthInstrs(); }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode*) const { return true; }
};
struct AstNodeBiop : public AstNodeMath {
// Binary math
AstNodeBiop(FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeMath(fl) {
setOp1p(lhs); setOp2p(rhs); }
virtual ~AstNodeBiop() {}
AstNode* lhsp() const { return op1p()->castNode(); }
AstNode* rhsp() const { return op2p()->castNode(); }
void lhsp(AstNode* nodep) { return setOp1p(nodep); }
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
// METHODS
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) = 0; // Set out to evaluation of a AstConst'ed
virtual bool cleanLhs() = 0; // True if LHS must have extra upper bits zero
virtual bool cleanRhs() = 0; // True if RHS must have extra upper bits zero
virtual bool sizeMattersLhs() = 0; // True if output result depends on lhs size
virtual bool sizeMattersRhs() = 0; // True if output result depends on rhs size
virtual bool signedFlavor() const { return false; } // Signed flavor of nodes with both flavors?
virtual int instrCount() const { return widthInstrs(); }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode*) const { return true; }
};
struct AstNodeTriop : public AstNodeMath {
// Trinary math
AstNodeTriop(FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths)
: AstNodeMath(fl) {
setOp1p(lhs); setOp2p(rhs); setOp3p(ths); }
virtual ~AstNodeTriop() {}
AstNode* lhsp() const { return op1p()->castNode(); }
AstNode* rhsp() const { return op2p()->castNode(); }
AstNode* thsp() const { return op3p()->castNode(); }
void lhsp(AstNode* nodep) { return setOp1p(nodep); }
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
void thsp(AstNode* nodep) { return setOp3p(nodep); }
// METHODS
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs, const V3Number& ths) = 0; // Set out to evaluation of a AstConst'ed
virtual bool cleanLhs() = 0; // True if LHS must have extra upper bits zero
virtual bool cleanRhs() = 0; // True if RHS must have extra upper bits zero
virtual bool cleanThs() = 0; // True if THS must have extra upper bits zero
virtual bool sizeMattersLhs() = 0; // True if output result depends on lhs size
virtual bool sizeMattersRhs() = 0; // True if output result depends on rhs size
virtual bool sizeMattersThs() = 0; // True if output result depends on ths size
virtual int instrCount() const { return widthInstrs(); }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode*) const { return true; }
};
struct AstNodeBiCom : public AstNodeBiop {
// Binary math with commutative properties
AstNodeBiCom(FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeBiop(fl, lhs, rhs) {}
virtual ~AstNodeBiCom() {}
};
struct AstNodeBiComAsv : public AstNodeBiCom {
// Binary math with commutative & associative properties
AstNodeBiComAsv(FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeBiCom(fl, lhs, rhs) {}
virtual ~AstNodeBiComAsv() {}
};
struct AstNodeCond : public AstNodeTriop {
AstNodeCond(FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p)
: AstNodeTriop(fl, condp, expr1p, expr2p) {
if (expr1p) widthSignedFrom(expr1p);
else if (expr2p) widthSignedFrom(expr2p);
}
virtual ~AstNodeCond() {}
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs, const V3Number& ths) {
if (lhs.isNeqZero()) out.opAssign(rhs); else out.opAssign(ths); }
AstNode* condp() const { return op1p()->castNode(); } // op1 = Condition
AstNode* expr1p() const { return op2p()->castNode(); } // op2 = If true...
AstNode* expr2p() const { return op3p()->castNode(); } // op3 = If false...
virtual string emitVerilog() { return "%k(%l %k? %r %k: %t)"; }
virtual string emitOperator() { return "VL_COND"; }
virtual bool cleanOut() { return false; } // clean if e1 & e2 clean
virtual bool cleanLhs() { return true; }
virtual bool cleanRhs() { return false; } virtual bool cleanThs() { return false; } // Propagates up
virtual bool sizeMattersLhs() { return false; } virtual bool sizeMattersRhs() { return false; }
virtual bool sizeMattersThs() { return false; }
virtual int instrCount() const { return instrCountBranch(); }
};
struct AstNodePreSel : public AstNode {
// Something that becomes a AstSel
AstNodePreSel(FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths)
: AstNode(fl) {
setOp1p(lhs); setOp2p(rhs); setNOp3p(ths); }
virtual ~AstNodePreSel() {}
AstNode* lhsp() const { return op1p()->castNode(); }
AstNode* rhsp() const { return op2p()->castNode(); }
AstNode* thsp() const { return op3p()->castNode(); }
void lhsp(AstNode* nodep) { return setOp1p(nodep); }
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
void thsp(AstNode* nodep) { return setOp3p(nodep); }
// METHODS
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode*) const { return true; }
};
struct AstNodeStmt : public AstNode {
// Statement -- anything that's directly under a function
AstNodeStmt(FileLine* fl)
: AstNode(fl) {}
virtual ~AstNodeStmt() {}
// METHODS
};
struct AstNodeAssign : public AstNodeStmt {
AstNodeAssign(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
: AstNodeStmt(fl) {
setOp1p(rhsp); setOp2p(lhsp);
if (lhsp) widthSignedFrom(lhsp);
}
virtual ~AstNodeAssign() {}
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp)=0; // Clone single node, just get same type back.
// So iteration hits the RHS which is "earlier" in execution order, it's op1, not op2
AstNode* rhsp() const { return op1p()->castNode(); } // op1 = Assign from
AstNode* lhsp() const { return op2p()->castNode(); } // op2 = Assign to
void rhsp(AstNode* np) { setOp1p(np); }
void lhsp(AstNode* np) { setOp2p(np); }
virtual bool cleanRhs() { return true; }
virtual int instrCount() const { return widthInstrs(); }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode*) const { return true; }
};
struct AstNodeFor : public AstNodeStmt {
AstNodeFor(FileLine* fileline, AstNode* initsp, AstNode* condp,
AstNode* assignsp, AstNode* bodysp)
: AstNodeStmt(fileline) {
addNOp1p(initsp); setOp2p(condp); addNOp3p(assignsp); addNOp4p(bodysp);
}
virtual ~AstNodeFor() {}
AstNode* initsp() const { return op1p()->castNode(); } // op1= initial statement
AstNode* condp() const { return op2p()->castNode(); } // op2= condition to continue
AstNode* assignsp() const { return op3p()->castNode(); } // op3= final statements
AstNode* bodysp() const { return op4p()->castNode(); } // op4= body of loop
virtual bool isGateOptimizable() const { return false; }
virtual bool isPredictOptimizable() const { return false; }
virtual int instrCount() const { return instrCountBranch(); }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode* samep) const { return true; }
};
struct AstNodeIf : public AstNodeStmt {
private:
AstBranchPred m_branchPred; // Branch prediction as taken/untaken?
public:
AstNodeIf(FileLine* fl, AstNode* condp, AstNode* ifsp, AstNode* elsesp)
: AstNodeStmt(fl) {
setOp1p(condp); addNOp2p(ifsp); addNOp3p(elsesp);
}
virtual ~AstNodeIf() {}
AstNode* condp() const { return op1p(); } // op1 = condition
AstNode* ifsp() const { return op2p(); } // op2 = list of true statements
AstNode* elsesp() const { return op3p(); } // op3 = list of false statements
void condp(AstNode* newp) { setOp1p(newp); }
void addIfsp(AstNode* newp) { addOp2p(newp); }
void addElsesp(AstNode* newp) { addOp3p(newp); }
virtual bool isGateOptimizable() const { return false; }
virtual int instrCount() const { return instrCountBranch(); }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode* samep) const { return true; }
void branchPred(AstBranchPred flag) { m_branchPred = flag; }
AstBranchPred branchPred() const { return m_branchPred; }
};
struct AstNodeCase : public AstNodeStmt {
AstNodeCase(FileLine* fl, AstNode* exprp, AstNode* casesp)
: AstNodeStmt(fl) {
setOp1p(exprp); addNOp2p(casesp);
}
virtual ~AstNodeCase() {}
virtual int instrCount() const { return instrCountBranch(); }
AstNode* exprp() const { return op1p()->castNode(); } // op1 = case condition <expression>
AstCaseItem* itemsp() const { return op2p()->castCaseItem(); } // op2 = list of case expressions
AstNode* notParallelp() const { return op3p()->castNode(); } // op3 = assertion code for non-full case's
void addItemsp(AstNode* nodep) { addOp2p(nodep); }
void addNotParallelp(AstNode* nodep) { setOp3p(nodep); }
};
class AstNodeVarRef : public AstNodeMath {
// A AstVarRef or AstVarXRef
private:
bool m_lvalue; // Left hand side assignment
AstVar* m_varp; // [AfterLink] Pointer to variable itself
AstVarScope* m_varScopep; // Varscope for hierarchy
string m_name; // Name of variable
string m_hiername; // Scope converted into name-> for emitting
bool m_hierThis; // Hiername points to "this" function
public:
AstNodeVarRef(FileLine* fl, const string& name, bool lvalue)
: AstNodeMath(fl), m_lvalue(lvalue), m_varp(NULL), m_varScopep(NULL),
m_name(name), m_hierThis(false) {
}
AstNodeVarRef(FileLine* fl, const string& name, AstVar* varp, bool lvalue)
: AstNodeMath(fl), m_lvalue(lvalue), m_varp(varp), m_varScopep(NULL),
m_name(name), m_hierThis(false) {
// May have varp==NULL
if (m_varp) widthSignedFrom((AstNode*)m_varp);
}
virtual ~AstNodeVarRef() {}
virtual bool broken() const;
virtual int instrCount() const { return widthInstrs(); }
virtual void cloneRelink();
virtual string name() const { return m_name; } // * = Var name
void name(const string& name) { m_name = name; }
bool lvalue() const { return m_lvalue; }
void lvalue(bool lval) { m_lvalue=lval; } // Avoid using this; Set in constructor
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
void varp(AstVar* varp) { m_varp=varp; }
AstVarScope* varScopep() const { return m_varScopep; }
void varScopep(AstVarScope* varscp) { m_varScopep=varscp; }
string hiername() const { return m_hiername; }
void hiername(const string& hn) { m_hiername = hn; }
bool hierThis() const { return m_hierThis; }
void hierThis(bool flag) { m_hierThis = flag; }
};
class AstNodePli : public AstNodeStmt {
string m_text;
public:
AstNodePli(FileLine* fl, const string& text, AstNode* exprsp)
: AstNodeStmt(fl), m_text(text) {
addNOp1p(exprsp); }
virtual ~AstNodePli() {}
virtual string name() const { return m_text; }
virtual int instrCount() const { return instrCountPli(); }
AstNode* exprsp() const { return op1p()->castNode(); } // op1 = Expressions to output
string text() const { return m_text; } // * = Text to display
void text(const string& text) { m_text=text; }
// op2p,op3p... used by AstDisplay
};
struct AstNodeText : public AstNode {
private:
string m_text;
public:
// Node that simply puts text into the output stream
AstNodeText(FileLine* fileline, const string& textp)
: AstNode(fileline) {
m_text = textp; // Copy it
}
virtual ~AstNodeText() {}
const string& text() const { return m_text; }
virtual V3Hash sameHash() const { return V3Hash(text()); }
virtual bool same(AstNode* samep) const {
return text()==samep->castNodeText()->text(); }
};
struct AstNodeSel : public AstNodeBiop {
// Single bit range extraction, perhaps with non-constant selection or array selection
AstNodeSel(FileLine* fl, AstNode* fromp, AstNode* bitp)
:AstNodeBiop(fl, fromp, bitp) {}
AstNode* fromp() const { return op1p()->castNode(); } // op1 = Extracting what (NULL=TBD during parsing)
AstNode* bitp() const { return op2p()->castNode(); } // op2 = Msb selection expression
int bitConst() const;
};
//######################################################################
// Tasks/functions common handling
struct AstNodeFTask : public AstNode {
private:
string m_name; // Name of task
bool m_taskPublic; // Public task
public:
// Node that simply puts name into the output stream
AstNodeFTask(FileLine* fileline, const string& name, AstNode* stmtsp)
: AstNode(fileline)
, m_name(name), m_taskPublic(false) {
addNOp3p(stmtsp);
}
virtual ~AstNodeFTask() {}
virtual void dump(ostream& str=cout);
virtual string name() const { return m_name; } // * = Var name
// {AstFunc only} op1 = Range output variable
// op3 = Statements/Ports/Vars
void name(const string& name) { m_name = name; }
AstNode* stmtsp() const { return op3p()->castNode(); } // op1 = List of statements
void addStmtsp(AstNode* nodep) { addOp3p(nodep); }
void taskPublic(bool flag) { m_taskPublic=flag; }
bool taskPublic() const { return m_taskPublic; }
};
struct AstNodeFTaskRef : public AstNode {
// A reference to a task (or function)
private:
AstNodeFTask* m_taskp; // [AfterLink] Pointer to task referenced
string m_name; // Name of variable
string m_dotted; // Dotted part of scope to task or ""
string m_inlinedDots; // Dotted hiearchy flattened out
public:
AstNodeFTaskRef(FileLine* fl, const string& name, const string& dotted, AstNode* pinsp)
:AstNode(fl)
, m_taskp(NULL), m_name(name), m_dotted(dotted) {
addNOp1p(pinsp);
}
virtual ~AstNodeFTaskRef() {}
virtual bool broken() const { return m_taskp && !m_taskp->brokeExists(); }
virtual void cloneRelink() { if (m_taskp && m_taskp->clonep()) {
m_taskp = m_taskp->clonep()->castNodeFTask();
}}
virtual void dump(ostream& str=cout);
virtual string name() const { return m_name; } // * = Var name
string dotted() const { return m_dotted; } // * = Scope name or ""
string prettyDotted() const { return prettyName(dotted()); }
string inlinedDots() const { return m_inlinedDots; }
void inlinedDots(const string& flag) { m_inlinedDots = flag; }
AstNodeFTask* taskp() const { return m_taskp; } // [After Link] Pointer to variable
void taskp(AstNodeFTask* taskp) { m_taskp=taskp; }
// op1 = Pin interconnection list
AstNode* pinsp() const { return op1p()->castNode(); }
void addPinsp(AstNode* nodep) { addOp1p(nodep); }
};
//######################################################################
#include "V3AstNodes.h"
#include "V3Ast__gen_impl.h" // From ./astgen
// Things like:
// inline AstAlways* AstNode::castAlways() { return dynamic_cast<AstAlways*>(this);}
//######################################################################
// Inline ACCESSORS
inline bool AstNode::isZero() { return (this->castConst() && this->castConst()->num().isEqZero()); }
inline bool AstNode::isNeqZero() { return (this->castConst() && this->castConst()->num().isNeqZero()); }
inline bool AstNode::isOne() { return (this->castConst() && this->castConst()->num().isEqOne()); }
inline bool AstNode::isAllOnes() { return (this->castConst() && this->castConst()->num().isEqAllOnes(this->width())); }
inline bool AstNode::isAllOnesV() { return (this->castConst() && this->castConst()->num().isEqAllOnes(this->widthMin())); }
#endif // Guard

388
src/V3AstNodes.cpp Normal file
View File

@ -0,0 +1,388 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Ast node structures
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include <stdio.h>
#include <stdarg.h>
#include <fstream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include "V3Ast.h"
#include "V3File.h"
#include "V3Global.h"
//======================================================================
// 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())); }
void AstNodeVarRef::cloneRelink() {
if (m_varp && m_varp->clonep()) { m_varp = m_varp->clonep()->castVar(); }
}
int AstNodeSel::bitConst() const {
AstConst* constp=bitp()->castConst(); return (constp?constp->asInt():0);
}
bool AstVar::isSigPublic() const {
return (m_sigPublic || (v3Global.opt.allPublic() && !isTemp()));
}
bool AstVar::isScQuad() const {
return (isSc()&&isQuad()&&v3Global.opt.pins64());
}
bool AstVar::isScWide() const {
return (isWide() || isSc()&&isQuad()&&!v3Global.opt.pins64());
}
void AstVar::combineType(AstVarType type) {
if (type == AstVarType::SUPPLY0) type = AstVarType::WIRE;
if (type == AstVarType::SUPPLY1) type = AstVarType::WIRE;
m_varType=type; // For debugging prints only
// These flags get combined with the existing settings of the flags.
if (type==AstVarType::INPUT || type==AstVarType::INOUT)
m_input = true;
if (type==AstVarType::OUTPUT || type==AstVarType::INOUT)
m_output = true;
if (type==AstVarType::INOUT || type==AstVarType::TRIWIRE)
m_tristate = true;
}
int AstVar::widthAlignBytes() const {
if (width()<=8) return 1;
else if (width()<=16) return 2;
else if (isSc() ? isScQuad() : isQuad()) return 8;
else return 4;
}
int AstVar::widthTotalBytes() const {
if (width()<=8) return 1;
else if (width()<=16) return 2;
else if (isSc() ? isScQuad() : isQuad()) return 8;
else return widthWords()*(VL_WORDSIZE/8);
}
string AstVar::verilogKwd() const {
if (isTristate()) {
return "inout";
} else if (isInput()) {
return "input";
} else if (isOutput()) {
return "output";
} else if (isInteger()) {
return "integer";
} else if (varType()==AstVarType::WIRE) {
return "wire";
} else {
return "reg";
}
}
string AstVar::cType() const {
if (widthMin() == 1) {
return "bool";
} else if (widthMin() <= VL_WORDSIZE) {
return "uint32_t";
} else if (isScWide()) {
return (string("sc_bv<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >>
} else {
return "uint64_t";
}
}
AstRange* AstVar::arrayp(int dimension) const {
for (AstRange* arrayp=this->arraysp(); arrayp; arrayp = arrayp->nextp()->castRange()) {
if ((dimension--)==0) return arrayp;
}
return NULL;
}
uint32_t AstVar::arrayElements() const {
uint32_t entries=1;
for (AstRange* arrayp=this->arraysp(); arrayp; arrayp = arrayp->nextp()->castRange()) {
entries *= arrayp->elementsConst();
}
return entries;
}
bool AstScope::broken() const {
return ((m_aboveScopep && !m_aboveScopep->brokeExists())
|| (m_aboveCellp && !m_aboveCellp->brokeExists())
|| !m_modp || !((AstNode*)m_modp)->brokeExists());
}
void AstScope::cloneRelink() {
if (m_aboveScopep && m_aboveScopep->clonep()) m_aboveScopep->clonep()->castScope();
if (m_aboveCellp && m_aboveCellp->clonep()) m_aboveCellp->clonep()->castCell();
if (m_modp && ((AstNode*)m_modp)->clonep()) ((AstNode*)m_modp)->clonep()->castModule();
}
string AstScope::nameDotless() const {
string dotless = shortName();
string::size_type pos;
while ((pos=dotless.find(".")) != string::npos) {
dotless.replace(pos, 1, "__");
}
return dotless;
}
struct AstSenItemCmp {
inline bool operator () (const AstSenItem* lhsp, const AstSenItem* rhsp) const {
// Looks visually better if we keep sorted by name
if (lhsp->edgeType() < rhsp->edgeType()) return true;
if (lhsp->edgeType() > rhsp->edgeType()) return false;
if (!lhsp->varrefp() && rhsp->varrefp()) return true;
if ( lhsp->varrefp() && !rhsp->varrefp()) return false;
if (lhsp->varrefp() && rhsp->varrefp()) {
if (lhsp->varrefp()->name() < rhsp->varrefp()->name()) return true;
if (lhsp->varrefp()->name() > rhsp->varrefp()->name()) return false;
// But might be same name with different scopes
if (lhsp->varrefp()->varScopep() < rhsp->varrefp()->varScopep()) return true;
if (lhsp->varrefp()->varScopep() > rhsp->varrefp()->varScopep()) return false;
}
return false;
}
};
void AstSenTree::sortSenses() {
// Sort the sensitivity names so "posedge a or b" and "posedge b or a" end up together.
// Also, remove duplicate assignments, and fold POS&NEGs into ANYEDGEs
//cout<<endl; this->dumpTree(cout,"ssin: ");
AstSenItem* nextp;
// Make things a little faster; check first if we need a sort
for (AstSenItem* senp = sensesp(); senp; senp=senp->nextp()->castSenItem()) {
nextp=senp->nextp()->castSenItem();
AstSenItemCmp cmp;
if (nextp && !cmp(senp, nextp)) {
// Something's out of order, sort it
senp = NULL;
vector<AstSenItem*> vec;
for (AstSenItem* senp = sensesp(); senp; senp=senp->nextp()->castSenItem()) {
vec.push_back(senp);
}
sort(vec.begin(), vec.end(), AstSenItemCmp());
for (vector<AstSenItem*>::iterator it=vec.begin(); it!=vec.end(); ++it) {
(*it)->unlinkFrBack();
}
for (vector<AstSenItem*>::iterator it=vec.begin(); it!=vec.end(); ++it) {
this->addSensesp(*it);
}
break;
}
}
// Pass2, remove dup edges
for (AstSenItem* senp = sensesp(); senp; senp=nextp) {
nextp=senp->nextp()->castSenItem();
AstSenItem* cmpp = nextp;
if (cmpp
&& ((senp->varrefp() && cmpp->varrefp() && senp->varrefp()->sameTree(cmpp->varrefp()))
|| (!senp->varrefp() && !cmpp->varrefp()))) {
// We've sorted in the order ANY, BOTH, POS, NEG, so we don't need to try opposite orders
if (( senp->edgeType()==AstEdgeType::ANYEDGE) // ANY or {BOTH|POS|NEG} -> ANY
|| (senp->edgeType()==AstEdgeType::BOTHEDGE) // BOTH or {POS|NEG} -> BOTH
|| (senp->edgeType()==AstEdgeType::POSEDGE // POS or NEG -> BOTH
&& cmpp->edgeType()==AstEdgeType::NEGEDGE)
|| (senp->edgeType()==cmpp->edgeType()) // Identical edges
) {
// Fix edge of old node
if (senp->edgeType()==AstEdgeType::POSEDGE
&& cmpp->edgeType()==AstEdgeType::NEGEDGE)
senp->edgeType(AstEdgeType::BOTHEDGE);
// Remove redundant node
cmpp->unlinkFrBack()->deleteTree(); cmpp=NULL;
// Try to collapse again
nextp=senp;
}
}
}
// Pass3, remove nevers
if (sensesp()->nextp()) { // Else only one never, can't remove it
for (AstSenItem* senp = sensesp(); senp; senp=nextp) {
nextp=senp->nextp()->castSenItem();
if (senp->isNever()) {
senp->unlinkFrBack()->deleteTree(); senp=NULL;
}
}
}
//this->dumpTree(cout,"ssou: ");
}
bool AstSenTree::hasClocked() {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
for (AstSenItem* senp = sensesp(); senp; senp=senp->nextp()->castSenItem()) {
if (senp->isClocked()) return true;
}
return false;
}
bool AstSenTree::hasSettle() {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
for (AstSenItem* senp = sensesp(); senp; senp=senp->nextp()->castSenItem()) {
if (senp->isSettle()) return true;
}
return false;
}
bool AstSenTree::hasInitial() {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
for (AstSenItem* senp = sensesp(); senp; senp=senp->nextp()->castSenItem()) {
if (senp->isInitial()) return true;
}
return false;
}
bool AstSenTree::hasCombo() {
if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it");
for (AstSenItem* senp = sensesp(); senp; senp=senp->nextp()->castSenItem()) {
if (senp->isCombo()) return true;
}
return false;
}
//======================================================================
// Per-type Debugging
void AstNode::dump(ostream& os) {
os<<typeName()<<" "<<(void*)this
//<<" "<<(void*)this->m_backp
<<" <e"<<dec<<editCount()
<<((editCount()>=editCountLast())?"#>":">")
<<" {"<<dec<<fileline()->lineno()<<"}"
<<" "<<(isSigned()?"s":"")
<<"w"<<(widthSized()?"":"u")<<width();
if (!widthSized()) os<<"/"<<widthMin();
if (name()!="") os<<" "<<name();
}
void AstCast::dump(ostream& str) {
this->AstNode::dump(str);
str<<" sz"<<size();
}
void AstCell::dump(ostream& str) {
this->AstNode::dump(str);
if (modp()) { str<<" -> "; modp()->dump(str); }
else { str<<" ->UNLINKED:"<<modName(); }
}
void AstCellInline::dump(ostream& str) {
this->AstNode::dump(str);
str<<" -> "<<origModName();
}
void AstPin::dump(ostream& str) {
this->AstNode::dump(str);
if (modVarp()) { str<<" -> "; modVarp()->dump(str); }
else { str<<" ->UNLINKED"; }
}
void AstVarXRef::dump(ostream& str) {
this->AstNode::dump(str);
if (lvalue()) str<<" [LV] => ";
else str<<" [RV] <- ";
str<<dotted()<<". - ";
if (inlinedDots()!="") str<<" flat.="<<inlinedDots()<<" - ";
if (varScopep()) { varScopep()->dump(str); }
else if (varp()) { varp()->dump(str); }
else { str<<"UNLINKED"; }
}
void AstModule::dump(ostream& str) {
this->AstNode::dump(str);
str<<" L"<<level();
if (modPublic()) str<<" [P]";
if (inLibrary()) str<<" [LIB]";
}
void AstVarScope::dump(ostream& str) {
this->AstNode::dump(str);
if (isCircular()) str<<" [CIRC]";
if (varp()) { str<<" -> "; varp()->dump(str); }
else { str<<" ->UNLINKED"; }
}
void AstVarRef::dump(ostream& str) {
this->AstNode::dump(str);
if (lvalue()) str<<" [LV] => ";
else str<<" [RV] <- ";
if (varScopep()) { varScopep()->dump(str); }
else if (varp()) { varp()->dump(str); }
else { str<<"UNLINKED"; }
}
void AstVar::dump(ostream& str) {
this->AstNode::dump(str);
if (isSc()) str<<" [SC]";
if (isInput()) str<<" [I]";
if (isPrimaryIO()) str<<(isInput()?" [PI]":" [PO]");
if (isOutput()) str<<" [O]";
if (isUsedClock()) str<<" [C]";
if (isSigPublic()) str<<" [P]";
if (attrClockEn()) str<<" [aCLKEN]";
if (attrFileDescr()) str<<" [aFD]";
if (isFuncLocal() || isFuncReturn()) str<<" [FUNC]";
str<<" "<<varType();
}
void AstSenTree::dump(ostream& str) {
this->AstNode::dump(str);
if (isMulti()) str<<" [MULTI]";
}
void AstSenItem::dump(ostream& str) {
this->AstNode::dump(str);
str<<" ["<<m_edgeType.ascii()<<"]";
}
void AstActive::dump(ostream& str) {
this->AstNode::dump(str);
str<<" => ";
if (sensesp()) { sensesp()->dump(str); }
else { str<<"UNLINKED"; }
}
void AstNodeFTaskRef::dump(ostream& str) {
this->AstNode::dump(str);
str<<" -> ";
if (dotted()!="") { str<<dotted()<<". - "; }
if (taskp()) { taskp()->dump(str); }
else { str<<"UNLINKED"; }
}
void AstNodeFTask::dump(ostream& str) {
this->AstNode::dump(str);
}
void AstCoverDecl::dump(ostream& str) {
this->AstNode::dump(str);
}
void AstCoverInc::dump(ostream& str) {
this->AstNode::dump(str);
str<<" -> ";
if (declp()) { declp()->dump(str); }
else { str<<"%Error:UNLINKED"; }
}
void AstTraceInc::dump(ostream& str) {
this->AstNode::dump(str);
str<<" -> ";
if (declp()) { declp()->dump(str); }
else { str<<"%Error:UNLINKED"; }
}
void AstCFile::dump(ostream& str) {
this->AstNode::dump(str);
if (source()) str<<" [SRC]";
if (slow()) str<<" [SLOW]";
}
void AstCCall::dump(ostream& str) {
this->AstNode::dump(str);
if (funcp()) {
str<<" "<<funcp()->name()<<" => ";
funcp()->dump(str);
}
}

2976
src/V3AstNodes.h Normal file

File diff suppressed because it is too large Load Diff

163
src/V3Begin.cpp Normal file
View File

@ -0,0 +1,163 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Removal of named begin blocks
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Begin's Transformations:
//
// Each module:
// Look for BEGINs
// BEGIN(VAR...) -> VAR ... {renamed}
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include <vector>
#include "V3Global.h"
#include "V3Begin.h"
#include "V3Inst.h"
#include "V3Ast.h"
//######################################################################
class BeginVisitor : public AstNVisitor {
private:
// STATE
AstModule* m_modp; // Current module
AstNodeFTask* m_ftaskp; // Current function/task
string m_beginScope; // Name of begin blocks above us
//int debug() { return 9; }
bool nameMatchesGen(const char* namep, string& numr) {
numr = "";
bool needbar = false;
for (const char* cp=namep; *cp; ) {
if (0==strncmp(cp,"genblk",6) || 0==strncmp(cp,"genfor",6)) {
cp += 6;
} else if (isdigit(*cp)) {
if (needbar) { numr += '_'; needbar = false; }
numr += *cp++;
} else if (*cp=='_') {
cp++;
needbar = true;
} else {
return false; // Not exact match
}
}
return true;
}
// VISITORS
virtual void visit(AstModule* nodep, AstNUser*) {
m_modp = nodep;
nodep->iterateChildren(*this);
m_modp = NULL;
}
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
m_ftaskp = nodep;
nodep->iterateChildren(*this);
m_ftaskp = NULL;
}
virtual void visit(AstBegin* nodep, AstNUser*) {
// Begin blocks were only useful in variable creation, change names and delete
UINFO(8," "<<nodep<<endl);
string oldScope = m_beginScope;
{
//string nameNum;
//string oldNum;
//if (nameMatchesGen(oldScope.c_str(), oldNum/*ref*/)
// && nameMatchesGen(nodep->name().c_str(), nameNum/*ref*/)
// && 0) { // Messes up V3Link
// // Need to leave the dot or we mess up later V3LinkDot
// // gen[blk|for]##_gen[blk|for]## -> gen[blk|for]##__DOT__##...
// m_beginScope = oldScope + "__DOT__"+nameNum;
//UINFO(8,"nname "<<m_beginScope<<endl);
// Create data for dotted variable resolution
string dottedname = nodep->name() + "__DOT__"; // So always found
string::size_type pos;
while ((pos=dottedname.find("__DOT__")) != string::npos) {
string ident = dottedname.substr(0,pos);
dottedname = dottedname.substr(pos+strlen("__DOT__"));
if (m_beginScope=="") m_beginScope = nodep->name();
else m_beginScope = m_beginScope + "__DOT__"+ident;
// Create CellInline for dotted resolution
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
m_beginScope, "__BEGIN__");
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
}
// Remap var names
nodep->iterateChildren(*this);
if (AstNode* stmtsp = nodep->stmtsp()) {
stmtsp->unlinkFrBackWithNext();
nodep->replaceWith(stmtsp);
} else {
nodep->unlinkFrBack();
}
pushDeletep(nodep); nodep=NULL;
}
m_beginScope = oldScope;
}
virtual void visit(AstVar* nodep, AstNUser*) {
if (m_beginScope != "") {
// Rename it
nodep->name(m_beginScope+"__DOT__"+nodep->name());
// Move to module
nodep->unlinkFrBack();
if (m_ftaskp) m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
else m_modp->addStmtp(nodep);
}
}
virtual void visit(AstCell* nodep, AstNUser*) {
UINFO(8," CELL "<<nodep<<endl);
if (m_beginScope != "") {
// Rename it
nodep->name(m_beginScope+"__DOT__"+nodep->name());
UINFO(8," rename to "<<nodep->name()<<endl);
// Move to module
nodep->unlinkFrBack();
m_modp->addStmtp(nodep);
}
}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
BeginVisitor(AstNetlist* nodep) {
m_modp = NULL;
m_ftaskp = NULL;
nodep->accept(*this);
}
virtual ~BeginVisitor() {}
};
//######################################################################
// Task class functions
void V3Begin::debeginAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
BeginVisitor bvisitor (nodep);
}

35
src/V3Begin.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Removal of named begin blocks
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3BEGIN_H_
#define _V3BEGIN_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Begin {
public:
static void debeginAll(AstNetlist* nodep);
};
#endif // Guard

105
src/V3Branch.cpp Normal file
View File

@ -0,0 +1,105 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Branch prediction
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// BRANCH TRANSFORMATIONS:
// At each IF/(IF else).
// Count underneath $display/$stop statements.
// If more on if then else, this branch is unlikely, or vice-versa.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <map>
#include "V3Global.h"
#include "V3Branch.h"
#include "V3Ast.h"
//######################################################################
// Branch state, as a visitor of each AstNode
class BranchVisitor : public AstNVisitor {
private:
// STATE
int m_likely; // Excuses for branch likely taken
int m_unlikely; // Excuses for branch likely not taken
//int debug() { return 9; }
// METHODS
void reset() {
m_likely = false;
m_unlikely = false;
}
// VISITORS
virtual void visit(AstNodeIf* nodep, AstNUser*) {
UINFO(4," IF: "<<nodep<<endl);
int lastLikely = m_likely;
int lastUnlikely = m_unlikely;
{
// Do if
reset();
nodep->ifsp()->iterateAndNext(*this);
int ifLikely = m_likely;
int ifUnlikely = m_unlikely;
// Do else
reset();
nodep->elsesp()->iterateAndNext(*this);
int elseLikely = m_likely;
int elseUnlikely = m_unlikely;
// Compute
int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
if (likeness>0) {
nodep->branchPred(AstBranchPred::LIKELY);
} else if (likeness<0) {
nodep->branchPred(AstBranchPred::UNLIKELY);
} // else leave unknown
}
m_likely = lastLikely;
m_unlikely = lastUnlikely;
}
virtual void visit(AstNode* nodep, AstNUser*) {
// Default: Just iterate
if (nodep->isUnlikely()) {
UINFO(4," UNLIKELY: "<<nodep<<endl);
m_unlikely++;
}
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
BranchVisitor(AstNetlist* rootp) {
reset();
rootp->iterateChildren(*this);
}
virtual ~BranchVisitor() {}
};
//######################################################################
// Branch class functions
void V3Branch::branchAll(AstNetlist* rootp) {
UINFO(2,__FUNCTION__<<": "<<endl);
BranchVisitor visitor (rootp);
}

36
src/V3Branch.h Normal file
View File

@ -0,0 +1,36 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Branch prediction
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3BRANCH_H_
#define _V3BRANCH_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Branch {
public:
// CREATORS
static void branchAll(AstNetlist* rootp);
};
#endif // Guard

131
src/V3Broken.cpp Normal file
View File

@ -0,0 +1,131 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Find broken links in tree
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Broken's Transformations:
//
// Entire netlist
// Mark all nodes
// Check all links point to marked nodes
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include <set>
#include "V3Global.h"
#include "V3Broken.h"
#include "V3Ast.h"
//######################################################################
class BrokenTable : public AstNVisitor {
// Table of brokenExists node pointers
private:
// MEMBERS
// For each node, we keep if it exists or not.
typedef set<const AstNode*> NodeSet;
static NodeSet s_nodes; // Set of all nodes that exist
public:
// METHODS
static void add(const AstNode* nodep) {
s_nodes.insert(nodep);
}
static bool exists(const AstNode* nodep) {
NodeSet::iterator iter = s_nodes.find(nodep);
return (iter != s_nodes.end());
}
static void clear() {
s_nodes.clear();
}
public:
// CONSTUCTORS
BrokenTable() {}
virtual ~BrokenTable() {}
};
BrokenTable::NodeSet BrokenTable::s_nodes;
bool AstNode::brokeExists() const {
// Called by node->broken() routines to do table lookup
return BrokenTable::exists(this);
}
//######################################################################
class BrokenMarkVisitor : public AstNVisitor {
// Mark every node in the tree
private:
// NODE STATE
// Nothing! // This may be called deep inside other routines
// // so userp and friends may not be used
// VISITORS
virtual void visit(AstNode* nodep, AstNUser*) {
BrokenTable::add(nodep);
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
BrokenMarkVisitor(AstNode* nodep) {
nodep->iterateAndNext(*this, NULL);
}
virtual ~BrokenMarkVisitor() {}
};
//######################################################################
// Broken state, as a visitor of each AstNode
class BrokenCheckVisitor : public AstNVisitor {
private:
virtual void visit(AstNode* nodep, AstNUser*) {
if (nodep->broken()) {
nodep->v3fatalSrc("Broken link in node\n");
}
if (v3Global.assertWidthsSame()) {
if (nodep->width() != nodep->widthMin()) {
nodep->v3fatalSrc("Width != WidthMin\n");
}
if (!nodep->width() && nodep->castNodeMath()) {
nodep->v3fatalSrc("Math node has no assigned width\n");
}
}
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
BrokenCheckVisitor(AstNetlist* nodep) {
nodep->accept(*this);
}
virtual ~BrokenCheckVisitor() {}
};
//######################################################################
// Broken class functions
void V3Broken::brokenAll(AstNetlist* nodep) {
//UINFO(9,__FUNCTION__<<": "<<endl);
BrokenTable::clear();
BrokenMarkVisitor mvisitor (nodep);
BrokenCheckVisitor cvisitor (nodep);
BrokenTable::clear();
}

35
src/V3Broken.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Find broken links in tree
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3BROKEN_H_
#define _V3BROKEN_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Broken {
public:
static void brokenAll(AstNetlist* nodep);
};
#endif // Guard

398
src/V3Case.cpp Normal file
View File

@ -0,0 +1,398 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Break case statements up and add Unknown assigns
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Case's Transformations:
//
// Each module:
// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel
// Need __en in changed list if a signal is on the LHS of a assign
// Cases:
// CASE(v) CASEITEM(items,body) -> IF (OR (EQ (AND v mask)
// (AND item1 mask))
// (other items))
// body
// Or, converts to a if/else tree.
// Constants:
// RHS, Replace 5'bx_1_x with a module global we init to a random value
// CONST(5'bx_1_x) -> VARREF(_{numberedtemp})
// -> VAR(_{numberedtemp})
// -> INITIAL(VARREF(_{numberedtemp}), OR(5'bx_1_x,AND(random,5'b0_1_x))
// OPTIMIZE: Must not collapse this initial back into the equation.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include "V3Global.h"
#include "V3Case.h"
#include "V3Ast.h"
#include "V3Stats.h"
#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in
//######################################################################
class CaseLintVisitor : public AstNVisitor {
private:
AstNodeCase* m_caseExprp; // Under a CASE value node, if so the relevant case statement
//int debug() { return 9; }
virtual void visit(AstNodeCase* nodep, AstNUser*) {
// We report a syntax error on empty "case (x) endcase" blocks, so never no items at all
if (!nodep->itemsp()) nodep->v3fatalSrc("No items (not even default) under case statement?\n");
// Detect multiple defaults
bool hitDefault = false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
if (itemp->isDefault()) {
if (hitDefault) {
nodep->v3error("Multiple default statements in case statement.");
}
hitDefault = true;
}
}
// Check for X/Z in non-casex statements
if (!nodep->castCase() || !nodep->castCase()->casex()) {
m_caseExprp = nodep;
nodep->exprp()->accept(*this);
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
itemp->condsp()->iterateAndNext(*this);
}
m_caseExprp = NULL;
}
}
virtual void visit(AstConst* nodep, AstNUser*) {
if (m_caseExprp && nodep->num().isFourState()) {
if (m_caseExprp->castGenCase()) {
nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')");
} else {
nodep->v3error("Use of x/? constant in case statement, (perhaps intended casex/casez)");
}
}
}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
CaseLintVisitor(AstNodeCase* nodep) {
m_caseExprp = NULL;
nodep->accept(*this);
}
virtual ~CaseLintVisitor() {}
};
//######################################################################
// Case state, as a visitor of each AstNode
class CaseVisitor : public AstNVisitor {
private:
// NODE STATE
// Cleared each Case
// AstIf::user3() -> bool. Set true to indicate clone not needed
// STATE
V3Double0 m_statCaseFast; // Statistic tracking
V3Double0 m_statCaseSlow; // Statistic tracking
// Per-CASE
int m_caseWidth; // Width of valueItems
int m_caseItems; // Number of caseItem unique values
int m_caseNoOverlapsAllCovered; // Proven to be synopsys parallel_case compliant
AstNode* m_valueItem[1<<CASE_OVERLAP_WIDTH]; // For each possible value, the case branch we need
//int debug() { return 9; }
// METHODS
bool checkCaseTree(AstCase* nodep) {
int width = 0;
m_caseItems = 0;
m_caseNoOverlapsAllCovered = true;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
if (icondp->width() > width) width = icondp->width();
if (!icondp->castConst()) width = 999; // Can't parse; not a constant
m_caseItems++;
}
}
if (width==0 || width > CASE_OVERLAP_WIDTH) {
m_caseNoOverlapsAllCovered = false;
return false; // Too wide for analysis
}
m_caseWidth = width;
UINFO(8,"Simple case statement: "<<nodep<<endl);
// Zero list of items for each value
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) m_valueItem[i] = NULL;
// Now pick up the values for each assignment
// We can cheat and use uint32_t's because we only support narrow case's
bool bitched = false;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
//if (debug()>=9) icondp->dumpTree(cout," caseitem: ");
AstConst* iconstp = icondp->castConst();
if (!iconstp) nodep->v3fatalSrc("above 'can't parse' should have caught this\n");
V3Number nummask (itemp->fileline(), iconstp->width());
nummask.opBitsNonX(iconstp->num());
uint32_t mask = nummask.asInt();
V3Number numval (itemp->fileline(), iconstp->width());
numval.opBitsOne(iconstp->num());
uint32_t val = numval.asInt();
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
if ((i & mask) == val) {
if (!m_valueItem[i]) {
m_valueItem[i] = itemp;
} else if (!itemp->ignoreOverlap() && !bitched) {
itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<<hex<<i<<")");
bitched = true;
m_caseNoOverlapsAllCovered = false;
}
}
}
}
// Defaults were moved to last in the caseitem list by V3Link
if (!itemp->condsp()) { // Case statement's default... Fill the table
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
if (!m_valueItem[i]) m_valueItem[i] = itemp;
}
}
}
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
if (!m_valueItem[i]) {
nodep->v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<<hex<<i<<")");
m_caseNoOverlapsAllCovered = false;
return false;
}
}
if (m_caseItems <= 3) return false; // Not worth simplifing
// Convert valueItem from AstCaseItem* to the expression
// Not done earlier, as we may now have a NULL because it's just a ";" NOP branch
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
m_valueItem[i] = m_valueItem[i]->castCaseItem()->bodysp();
}
return true; // All is fine
}
AstNode* replaceCaseFastRecurse(AstNode* cexprp, int msb, uint32_t upperValue) {
if (msb<0) {
// There's no space for a IF. We know upperValue is thus down to a specific
// exact value, so just return the tree value
return m_valueItem[upperValue];
}
else {
// Make left and right subtrees
// cexpr[msb:lsb] == 1
AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | 0);
AstNode* tree1p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | (1UL<<msb));
if (tree0p == tree1p) {
// Same logic on both sides, so we can just return one of em
return tree0p;
}
// We could have a "checkerboard" with A B A B, we can use the same IF on both edges
bool same = true;
for (uint32_t a=upperValue,
b=(upperValue|(1UL<<msb));
a < (upperValue|(1UL<<msb));
a++, b++) {
if (m_valueItem[a] != m_valueItem[b]) { same=false; break; }
}
if (same) return tree0p;
// Must have differing logic, so make a selection
// Case expressions can't be linked twice, so clone them
if (tree0p && !tree0p->user3()) tree0p = tree0p->cloneTree(true);
if (tree1p && !tree1p->user3()) tree1p = tree1p->cloneTree(true);
// Alternate scheme if we ever do multiple bits at a time:
//V3Number nummask (cexprp->fileline(), cexprp->width(), (1UL<<msb));
//AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTree(false),
// new AstConst(cexprp->fileline(), nummask));
AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false),
msb, 1);
AstNode* eqp = new AstNeq(cexprp->fileline(),
new AstConst(cexprp->fileline(), 0),
and1p);
AstIf* ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p);
ifp->user3(1); // So we don't bother to clone it
return ifp;
}
}
void replaceCaseFast(AstCase* nodep) {
// CASEx(cexpr,....
// -> tree of IF(msb, IF(msb-1, 11, 10)
// IF(msb-1, 01, 00))
AstNode* cexprp = nodep->exprp();
cexprp->unlinkFrBack();
if (debug()>=9) {
for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
if (AstNode* itemp = m_valueItem[i]) {
UINFO(9,"Value "<<hex<<i<<" "<<itemp<<endl);
}
}
}
// Handle any assertions
replaceCaseParallel(nodep, m_caseNoOverlapsAllCovered);
AstNode::user3ClearTree();
AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth-1, 0UL);
if (ifrootp) nodep->replaceWith(ifrootp);
else nodep->unlinkFrBack();
if (debug()>=9) ifrootp->dumpTree(cout," _simp: ");
}
void replaceCaseComplicated(AstCase* nodep) {
// CASEx(cexpr,ITEM(icond1,istmts1),ITEM(icond2,istmts2),ITEM(default,istmts3))
// -> IF((cexpr==icond1),istmts1,
// IF((EQ (AND MASK cexpr) (AND MASK icond1)
// ,istmts2, istmts3
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
AstNode* ifrootp = NULL;
AstNode* ifnextp = NULL;
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action.
if (istmtsp) istmtsp->unlinkFrBackWithNext();
if (!itemp->condsp()) {
// Default clause. We reordered in link, so default is always last
if (ifnextp) {
if (istmtsp) ifnextp->castIf()->addElsesp(istmtsp);
} else { // Just a empty case default: endcase
ifnextp = istmtsp;
}
if (!ifrootp) ifrootp = istmtsp;
ifnextp = istmtsp;
itemp = NULL; break;
} else {
// Expressioned clause
AstNode* icondNextp = NULL;
AstNode* ifexprp = NULL; // If expression to test
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondNextp) {
icondNextp = icondp->nextp();
icondp->unlinkFrBack();
AstNode* and1p;
AstNode* and2p;
AstConst* iconstp = icondp->castConst();
if (iconstp && iconstp->num().isFourState()
&& nodep->casex()) {
V3Number nummask (itemp->fileline(), iconstp->width());
nummask.opBitsNonX(iconstp->num());
V3Number numval (itemp->fileline(), iconstp->width());
numval.opBitsOne(iconstp->num());
and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
new AstConst(itemp->fileline(), nummask));
and2p = new AstAnd(itemp->fileline(),
new AstConst(itemp->fileline(), numval),
new AstConst(itemp->fileline(), nummask));
} else {
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
and1p = cexprp->cloneTree(false);
and2p = icondp;
}
AstEq* condp = new AstEq(itemp->fileline(), and1p, and2p);
if (!ifexprp) {
ifexprp = condp;
} else {
ifexprp = new AstLogOr(itemp->fileline(), ifexprp, condp);
}
}
// Make the new IF and attach in the tree
if (ifexprp) {
AstIf* newp = new AstIf(itemp->fileline(), ifexprp, istmtsp, NULL);
if (ifnextp) {
ifnextp->castIf()->addElsesp(newp);
}
if (!ifrootp) ifrootp = newp;
ifnextp = newp;
}
}
}
// Handle any assertions
replaceCaseParallel(nodep, false);
// Replace the CASE... with IF...
if (ifrootp) nodep->replaceWith(ifrootp);
else nodep->unlinkFrBack();
if (debug()>=9) ifrootp->dumpTree(cout," _new: ");
}
void replaceCaseParallel(AstCase* nodep, bool noOverlapsAllCovered) {
// Take the notParallelp tree under the case statement created by V3Assert
// If the statement was proven to have no overlaps and all cases covered, we're done with it.
// Else, convert to a normal statement parallel with the case statement.
if (nodep->notParallelp() && !noOverlapsAllCovered) {
AstNode* parp = nodep->notParallelp()->unlinkFrBackWithNext();
nodep->addNextHere(parp);
}
}
// VISITORS
virtual void visit(AstCase* nodep, AstNUser*) {
V3Case::caseLint(nodep);
nodep->iterateChildren(*this);
if (debug()>=9) nodep->dumpTree(cout," case_old: ");
if (checkCaseTree(nodep) && v3Global.opt.oCase()) {
// It's a simple priority encoder or complete statement
// we can make a tree of statements to avoid extra comparisons
m_statCaseFast++;
replaceCaseFast(nodep); nodep=NULL;
} else {
m_statCaseSlow++;
replaceCaseComplicated(nodep); nodep=NULL;
}
}
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
CaseVisitor(AstNode* nodep) {
m_caseNoOverlapsAllCovered = false;
nodep->accept(*this);
}
virtual ~CaseVisitor() {
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
V3Stats::addStat("Optimizations, Cases priority-encoded", m_statCaseSlow);
}
};
//######################################################################
// Case class functions
void V3Case::caseAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
CaseVisitor visitor (nodep);
}
void V3Case::caseLint(AstNodeCase* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
CaseLintVisitor visitor (nodep);
}

36
src/V3Case.h Normal file
View File

@ -0,0 +1,36 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Break case statements up and add Unknown assigns
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3CASE_H_
#define _V3CASE_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Case {
public:
static void caseAll(AstNetlist* nodep);
static void caseLint(AstNodeCase* nodep);
};
#endif // Guard

167
src/V3Cast.cpp Normal file
View File

@ -0,0 +1,167 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Add C++ casts across expression size changes
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2004-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Cast's Transformations:
//
// Each module:
// For each math operator, if above operator requires 32 bits,
// and this isn't, cast to 32 bits.
// Likewise for 64 bit operators.
//
// C++ rules:
// Integral promotions allow conversion to larger int. Unsigned is only
// used if a int would not fit the value.
//
// Bools converts to int, not unsigned.
//
// Most operations return unsigned if either operand is unsigned.
//
// Unsignedness can be lost on results of the below operations, as they
// may need the sign bit for proper operation:
// /, %, /=, %=, <, <=, >, or >=
//
// Signed values are always sign extended on promotion or right shift,
// even if assigning to a unsigned.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include "V3Global.h"
#include "V3Cast.h"
#include "V3Ast.h"
//######################################################################
// Cast state, as a visitor of each AstNode
class CastVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist:
// STATE
//int debug() { return 9; }
// METHODS
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
UINFO(4," NeedCast "<<nodep<<endl);
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
//
AstCast* castp = new AstCast (nodep->fileline(), nodep, needsize);
castp->width(needsize, nodep->widthMin());
relinkHandle.relink(castp);
//if (debug()>8) castp->dumpTree(cout,"-castins: ");
//
insureLower32Cast(castp);
}
int castSize (AstNode* nodep) {
if (nodep->isQuad()) return VL_QUADSIZE;
else if (nodep->width()<=8) return 8;
else if (nodep->width()<=16) return 16;
else return VL_WORDSIZE;
}
void insureCast(AstNode* nodep) {
if (castSize(nodep->backp()) != castSize(nodep)) {
insertCast(nodep, castSize(nodep->backp()));
}
}
void insureLower32Cast(AstCast* nodep) {
// If we have uint64 = CAST(uint64(x)) then the upcasting
// really needs to be CAST(uint64(CAST(uint32(x))).
// Otherwise a (uint64)(a>b) would return wrong value, as
// less than has undeterministic signedness.
if (nodep->isQuad() && !nodep->lhsp()->isQuad()
&& !nodep->lhsp()->castCast()) {
insertCast(nodep->lhsp(), VL_WORDSIZE);
}
}
// VISITORS
virtual void visit(AstNodeUniop* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
}
virtual void visit(AstNodeBiop* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
}
virtual void visit(AstNodeTriop* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp());
if (nodep->sizeMattersThs()) insureCast(nodep->thsp());
}
virtual void visit(AstCast* nodep, AstNUser*) {
nodep->iterateChildren(*this);
insureLower32Cast(nodep);
}
virtual void visit(AstUnaryMin* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->lhsp()->widthMin()==1) {
// We want to avoid a GCC "converting of negative value" warning
// from our expansion of
// out = {32{a<b}} => out = - (a<b)
insertCast(nodep->lhsp(), castSize(nodep));
} else {
insureCast(nodep->lhsp());
}
}
virtual void visit(AstVarRef* nodep, AstNUser*) {
if (!nodep->lvalue()
&& !nodep->backp()->castCast()
&& nodep->backp()->castNodeMath()
&& nodep->backp()->width()
&& castSize(nodep) != castSize(nodep->varp())) {
// Cast vars to IData first, else below has upper bits wrongly set
// CData x=3; out = (QData)(x<<30);
insertCast (nodep, castSize(nodep));
}
}
// NOPs
virtual void visit(AstVar* nodep, AstNUser*) {}
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
CastVisitor(AstNetlist* nodep) {
nodep->accept(*this);
}
virtual ~CastVisitor() {}
};
//######################################################################
// Cast class functions
void V3Cast::castAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
CastVisitor visitor (nodep);
}

35
src/V3Cast.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Add C++ casts across expression size changes
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2004-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3CAST_H_
#define _V3CAST_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Cast {
public:
static void castAll(AstNetlist* nodep);
};
#endif // Guard

147
src/V3Changed.cpp Normal file
View File

@ -0,0 +1,147 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Add temporaries, such as for changed nodes
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Changed's Transformations:
//
// Each module:
// Each combo block
// For each variable that comes from combo block and is generated AFTER a usage
// Add __Vlast_{var} to local section, init to current value (just use array?)
// Change = if any var != last.
// If a signal is used as a clock in this module or any
// module *below*, and it isn't a input to this module,
// we need to indicate a new clock has been created.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include <set>
#include "V3Global.h"
#include "V3Changed.h"
#include "V3Ast.h"
//######################################################################
// Changed state, as a visitor of each AstNode
class ChangedVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist:
// AstVarScope::user() -> bool. True indicates processed
// STATE
AstModule* m_topModp; // Top module
AstScope* m_scopetopp; // Scope under TOPSCOPE
AstCFunc* m_chgFuncp; // Change function we're building
//int debug() { return 9; }
// METHODS
void genChangeDet(AstVarScope* vscp) {
#ifdef NEW_ORDERING
vscp->v3fatalSrc("Not applicable\n");
#endif
AstVar* varp = vscp->varp();
if (varp->arraysp()) {
vscp->v3error("Unsupported: Can't detect changes on arrayed variable (probably with UNOPTFLAT warning suppressed): "<<varp->prettyName());
} else {
string newvarname = "__Vchglast__"+vscp->scopep()->nameDotless()+"__"+varp->shortName();
// Create: VARREF(_last)
// ASSIGN(VARREF(_last), VARREF(var))
// ...
// CHANGEDET(VARREF(_last), VARREF(var))
AstVar* newvarp = new AstVar (varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
m_topModp->addStmtp(newvarp);
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopetopp, newvarp);
m_scopetopp->addVarp(newvscp);
AstChangeDet* changep
= new AstChangeDet (vscp->fileline(),
new AstVarRef(vscp->fileline(), vscp, false),
new AstVarRef(vscp->fileline(), newvscp, false),
false);
m_chgFuncp->addStmtsp(changep);
AstAssign* initp
= new AstAssign (vscp->fileline(),
new AstVarRef(vscp->fileline(), newvscp, true),
new AstVarRef(vscp->fileline(), vscp, false));
m_chgFuncp->addFinalsp(initp);
}
}
// VISITORS
virtual void visit(AstModule* nodep, AstNUser*) {
UINFO(4," MOD "<<nodep<<endl);
if (nodep->isTop()) {
m_topModp = nodep;
}
nodep->iterateChildren(*this);
}
virtual void visit(AstTopScope* nodep, AstNUser*) {
UINFO(4," TS "<<nodep<<endl);
// Clearing
AstNode::userClearTree();
// Create the change detection function
AstScope* scopep = nodep->scopep()->castScope();
if (!scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?\n");
m_scopetopp = scopep;
// Create change detection function
m_chgFuncp = new AstCFunc(nodep->fileline(), "_change_request", scopep, "bool");
m_scopetopp->addActivep(m_chgFuncp);
// We need at least one change detect so we know to emit the correct code
m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false));
//
nodep->iterateChildren(*this);
}
virtual void visit(AstVarScope* nodep, AstNUser*) {
if (nodep->isCircular()) {
UINFO(8," CIRC "<<nodep<<endl);
if (!nodep->user()) {
nodep->user(true);
genChangeDet(nodep);
}
}
}
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
ChangedVisitor(AstNetlist* nodep) {
m_topModp = NULL;
m_chgFuncp = NULL;
m_scopetopp = NULL;
nodep->accept(*this);
}
virtual ~ChangedVisitor() {}
};
//######################################################################
// Changed class functions
void V3Changed::changedAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
ChangedVisitor visitor (nodep);
}

35
src/V3Changed.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Pre C-Emit stage changes
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3CHANGED_H_
#define _V3CHANGED_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Changed {
public:
static void changedAll(AstNetlist* nodep);
};
#endif // Guard

268
src/V3Clean.cpp Normal file
View File

@ -0,0 +1,268 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Add temporaries, such as for clean nodes
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Clean's Transformations:
//
// Each module:
// For each math operator, if it requires a clean operand,
// and the operand is dirty, insert a CLEAN node.
// Resize operands to C++ 32/64/wide types.
// Copy all width() values to minWidth() so RANGE, etc can still see orig widths
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include "V3Global.h"
#include "V3Clean.h"
#include "V3Ast.h"
//######################################################################
// Clean state, as a visitor of each AstNode
class CleanVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist:
// AstNode::user() -> CleanState. For this node, 0==UNKNOWN
// AstNode::user2() -> bool. True indicates minWidth has been propagated
// STATE
AstModule* m_modp;
//int debug() { return 9; }
// ENUMS
enum CleanState { UNKNOWN, CLEAN, DIRTY };
// METHODS
// Width resetting
int cppWidth(AstNode* nodep) {
if (nodep->width()<=VL_WORDSIZE) return VL_WORDSIZE;
else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE;
else return nodep->widthWords()*VL_WORDSIZE;
}
void setCppWidth (AstNode* nodep, int width, int widthMin) {
nodep->user2(true); // Don't resize it again
nodep->width(width, widthMin);
}
void computeCppWidth (AstNode* nodep) {
if (!nodep->user2()) {
if (nodep->castVar()) { // Don't want to change variable widths!
setCppWidth(nodep, nodep->width(), nodep->width()); // set widthMin anyways so can see it later
} else {
setCppWidth(nodep, cppWidth(nodep), nodep->widthMin());
}
}
}
// Store the clean state in the userp on each node
void setCleanState(AstNode* nodep, CleanState clean) {
nodep->user(clean);
}
CleanState getCleanState(AstNode* nodep) {
return ((CleanState)nodep->user());
}
bool isClean(AstNode* nodep) {
CleanState clstate = getCleanState(nodep);
if (clstate==CLEAN) return true;
if (clstate==DIRTY) return false;
nodep->v3fatalSrc("Unknown clean state on node.");
return false;
}
void setClean(AstNode* nodep, bool isClean) {
computeCppWidth (nodep); // Just to be sure it's in widthMin
bool wholeUint = ((nodep->widthMin() % VL_WORDSIZE) == 0); //32,64,...
setCleanState(nodep, ((isClean||wholeUint) ? CLEAN:DIRTY));
}
// Operate on nodes
void insertClean(AstNode* nodep) { // We'll insert ABOVE passed node
UINFO(4," NeedClean "<<nodep<<endl);
AstNRelinker relinkHandle;
nodep->unlinkFrBack(&relinkHandle);
//
computeCppWidth(nodep);
V3Number zero (nodep->fileline(), nodep->widthMin(),0);
V3Number masksmall (nodep->fileline(), nodep->widthMin());
masksmall.opNot(zero);
V3Number mask (nodep->fileline(), cppWidth(nodep));
mask.opAssign(masksmall);
AstNode* cleanp = new AstAnd (nodep->fileline(),
new AstConst (nodep->fileline(), mask),
nodep);
setCppWidth (cleanp, cppWidth(nodep), nodep->widthMin());
relinkHandle.relink(cleanp);
}
void insureClean(AstNode* nodep) {
computeCppWidth(nodep);
if (!isClean(nodep)) insertClean(nodep);
}
void insureCleanAndNext(AstNode* nodep) {
// Editing list, careful looping!
for (AstNode* exprp = nodep; exprp; ) {
AstNode* nextp = exprp->nextp();
insureClean(exprp);
exprp = nextp;
}
}
// Base type handling methods
void operandBiop(AstNodeBiop* nodep) {
nodep->iterateChildren(*this);
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
}
if (nodep->cleanRhs()) {
insureClean(nodep->rhsp());
}
//no setClean.. must do it in each user routine.
}
void operandTriop(AstNodeTriop* nodep) {
nodep->iterateChildren(*this);
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
}
if (nodep->cleanRhs()) {
insureClean(nodep->rhsp());
}
if (nodep->cleanThs()) {
insureClean(nodep->thsp());
}
//no setClean.. must do it in each user routine.
}
// VISITORS
virtual void visit(AstModule* nodep, AstNUser*) {
m_modp = nodep;
nodep->iterateChildren(*this);
}
virtual void visit(AstNodeUniop* nodep, AstNUser*) {
nodep->iterateChildren(*this);
computeCppWidth(nodep);
if (nodep->cleanLhs()) {
insureClean(nodep->lhsp());
}
setClean (nodep, nodep->cleanOut());
}
virtual void visit(AstNodeBiop* nodep, AstNUser*) {
operandBiop(nodep);
setClean (nodep, nodep->cleanOut());
}
virtual void visit(AstAnd* nodep, AstNUser*) {
operandBiop(nodep);
setClean (nodep, isClean(nodep->lhsp()) || isClean(nodep->rhsp()));
}
virtual void visit(AstXor* nodep, AstNUser*) {
operandBiop(nodep);
setClean (nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
}
virtual void visit(AstOr* nodep, AstNUser*) {
operandBiop(nodep);
setClean (nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
}
virtual void visit(AstNodeMath* nodep, AstNUser*) {
nodep->iterateChildren(*this);
computeCppWidth(nodep);
setClean (nodep, nodep->cleanOut());
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
nodep->iterateChildren(*this);
computeCppWidth(nodep);
if (nodep->cleanRhs()) {
insureClean(nodep->rhsp());
}
}
virtual void visit(AstText* nodep, AstNUser*) {
setClean (nodep, true);
}
virtual void visit(AstSel* nodep, AstNUser*) {
operandTriop(nodep);
setClean (nodep, nodep->cleanOut());
}
virtual void visit(AstUCFunc* nodep, AstNUser*) {
nodep->iterateChildren(*this);
computeCppWidth(nodep);
setClean (nodep, false);
// We always clean, as we don't trust those pesky users.
if (!nodep->backp()->castAnd()) {
insertClean(nodep);
}
insureCleanAndNext (nodep->bodysp());
}
virtual void visit(AstTraceInc* nodep, AstNUser*) {
nodep->iterateChildren(*this);
insureCleanAndNext (nodep->valuep());
}
// Control flow operators
virtual void visit(AstNodeCond* nodep, AstNUser*) {
nodep->iterateChildren(*this);
insureClean(nodep->condp());
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
}
virtual void visit(AstWhile* nodep, AstNUser*) {
nodep->iterateChildren(*this);
insureClean(nodep->condp());
}
virtual void visit(AstNodeIf* nodep, AstNUser*) {
nodep->iterateChildren(*this);
insureClean(nodep->condp());
}
virtual void visit(AstNodePli* nodep, AstNUser*) {
nodep->iterateChildren(*this);
insureCleanAndNext (nodep->exprsp());
}
virtual void visit(AstUCStmt* nodep, AstNUser*) {
nodep->iterateChildren(*this);
insureCleanAndNext (nodep->bodysp());
}
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
computeCppWidth(nodep);
}
public:
// CONSTUCTORS
CleanVisitor() {}
virtual ~CleanVisitor() {}
void main(AstNetlist* nodep) {
AstNode::userClearTree(); // userp() used on entire tree
AstNode::user2ClearTree(); // userp() used on entire tree
nodep->accept(*this);
}
};
//######################################################################
// Clean class functions
void V3Clean::cleanAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
CleanVisitor visitor;
visitor.main(nodep);
}

35
src/V3Clean.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Pre C-Emit stage changes
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3CLEAN_H_
#define _V3CLEAN_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Clean {
public:
static void cleanAll(AstNetlist* nodep);
};
#endif // Guard

491
src/V3Clock.cpp Normal file
View File

@ -0,0 +1,491 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Clocking POS/NEGEDGE insertion
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Clock's Transformations:
//
// Top Scope:
// Check created ACTIVEs
// Compress adjacent ACTIVEs with same sensitivity list
// Form master _eval function
// Add around the SENTREE a (IF POSEDGE(..))
// Add a __Vlast_{clock} for the comparison
// Set the __Vlast_{clock} at the end of the block
// Replace UNTILSTABLEs with loops until specified signals become const.
// Create global calling function for any per-scope functions. (For FINALs).
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include "V3Global.h"
#include "V3Clock.h"
#include "V3Ast.h"
//######################################################################
// Clock state, as a visitor of each AstNode
class ClockVisitor : public AstNVisitor {
private:
// NODE STATE
// Cleared each Module:
// AstVarScope::userp() -> AstVarScope*. Temporary signal that was created.
// AstVarScope::user2p() -> AstVarScope*. Temporary signal for change detects
// TYPES
enum { DOUBLE_OR_RATE = 10 }; // How many | per ||, Determined experimentally as best
// STATE
AstModule* m_modp; // Current module
AstTopScope* m_topScopep; // Current top scope
AstScope* m_scopep; // Current scope
AstActive* m_activep; // Current block
AstUntilStable* m_untilp; // Current until
AstCFunc* m_evalFuncp; // Top eval function we are creating
AstCFunc* m_initFuncp; // Top initial function we are creating
AstCFunc* m_finalFuncp; // Top final function we are creating
AstCFunc* m_settleFuncp; // Top settlement function we are creating
AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates.
AstIf* m_lastIfp; // Last sensitivity if active to add more under
int m_stableNum; // Number of each untilstable
//int debug() { return 9; }
// METHODS
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
if (vscp->userp()) return ((AstVarScope*)vscp->userp());
AstVar* varp = vscp->varp();
if (varp->width()!=1) varp->v3error("Unsupported: Clock edge on non-single bit signal: "<<varp->prettyName());
string newvarname = ((string)"__Vclklast__"+vscp->scopep()->nameDotless()+"__"+varp->shortName());
AstVar* newvarp
= new AstVar (vscp->fileline(), AstVarType::MODULETEMP, newvarname); // No range; 1 bit.
newvarp->width(1,1);
m_modp->addStmtp(newvarp);
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
vscp->userp(newvscp);
m_scopep->addVarp(newvscp);
// At bottom, assign them
AstAssign* finalp
= new AstAssign (vscp->fileline(),
new AstVarRef(vscp->fileline(), newvscp, true),
new AstVarRef(vscp->fileline(), vscp, false));
m_evalFuncp->addFinalsp(finalp);
//
UINFO(4,"New Last: "<<newvscp<<endl);
return newvscp;
}
AstVarScope* getCreateLocalVar(FileLine* fl, const string& name, AstVar* examplep, int width) {
AstVar* newvarp;
if (width) {
newvarp = new AstVar (fl, AstVarType::BLOCKTEMP, name, new AstRange(fl, width-1, 0));
} else {
newvarp = new AstVar (fl, AstVarType::BLOCKTEMP, name, examplep); // No range; 1 bit.
}
m_modp->addStmtp(newvarp);
AstVarScope* newvscp = new AstVarScope(fl, m_scopep, newvarp);
m_scopep->addVarp(newvscp);
return newvscp;
}
AstNode* createSenseEquation(AstSenTree* nodep) {
AstNode* senEqnp = NULL;
for (AstSenItem* senp = nodep->sensesp(); senp; senp=senp->nextp()->castSenItem()) {
// We know the var is clean, and one bit, so we use binary ops
// for speed instead of logical ops.
// POSEDGE: var & ~var_last
// NEGEDGE: ~var & var_last
// BOTHEDGE: var ^ var_last
// HIGHEDGE: var
// LOWEDGE: ~var
AstVarScope* lastVscp = senp->varrefp()->varScopep()->userp()->castNode()->castVarScope();
AstNode* senOnep = NULL;
if (senp->edgeType()==AstEdgeType::POSEDGE) {
if (!lastVscp) senp->v3fatalSrc("No last var ptr?\n");
senOnep = new AstAnd(senp->fileline(),
new AstVarRef(senp->fileline(),
senp->varrefp()->varScopep(), false),
new AstNot(senp->fileline(),
new AstVarRef(senp->fileline(),
lastVscp, false)));
} else if (senp->edgeType()==AstEdgeType::NEGEDGE) {
if (!lastVscp) senp->v3fatalSrc("No last var ptr?\n");
senOnep = new AstAnd(senp->fileline(),
new AstNot(senp->fileline(),
new AstVarRef(senp->fileline(),
senp->varrefp()->varScopep(), false)),
new AstVarRef(senp->fileline(), lastVscp, false));
} else if (senp->edgeType()==AstEdgeType::BOTHEDGE) {
if (!lastVscp) senp->v3fatalSrc("No last var ptr?\n");
senOnep = new AstXor(senp->fileline(),
new AstVarRef(senp->fileline(),
senp->varrefp()->varScopep(), false),
new AstVarRef(senp->fileline(), lastVscp, false));
} else if (senp->edgeType()==AstEdgeType::HIGHEDGE) {
senOnep = new AstVarRef(senp->fileline(),
senp->varrefp()->varScopep(), false);
} else if (senp->edgeType()==AstEdgeType::LOWEDGE) {
senOnep = new AstNot(senp->fileline(),
new AstVarRef(senp->fileline(),
senp->varrefp()->varScopep(), false));
} else {
senp->v3fatalSrc("Bad edge type");
}
if (senEqnp) {
// Add new OR to the sensitivity list equation
senEqnp = new AstOr(senp->fileline(), senEqnp, senOnep);
} else {
senEqnp = senOnep;
}
}
return senEqnp;
}
AstIf* makeActiveIf(AstSenTree* sensesp) {
for (AstSenItem* senp = sensesp->sensesp(); senp; senp=senp->nextp()->castSenItem()) {
if (senp->edgeType() != AstEdgeType::HIGHEDGE
&& senp->varrefp()) {
AstVarScope* oldvscp = senp->varrefp()->varScopep();
getCreateLastClk(oldvscp);
}
}
AstNode* senEqnp = createSenseEquation(sensesp);
if (!senEqnp) sensesp->v3fatalSrc("No sense equation, shouldn't be in sequent activation.");
AstIf* newifp = new AstIf (sensesp->fileline(),
senEqnp, NULL, NULL);
return (newifp);
}
void clearLastSen() {
m_lastSenp = NULL;
m_lastIfp = NULL;
}
// VISITORS
virtual void visit(AstTopScope* nodep, AstNUser*) {
UINFO(4," TOPSCOPE "<<nodep<<endl);
m_topScopep=nodep;
m_scopep = nodep->scopep()->castScope();
if (!m_scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?\n");
//VV***** We reset all userp()
AstNode::userClearTree();
// Make top functions
{
m_evalFuncp = new AstCFunc(nodep->fileline(), "_eval", m_scopep);
m_evalFuncp->dontCombine(true);
m_scopep->addActivep(m_evalFuncp);
}
{
m_initFuncp = new AstCFunc(nodep->fileline(), "_eval_initial", m_scopep);
m_initFuncp->dontCombine(true);
m_initFuncp->slow(true);
m_scopep->addActivep(m_initFuncp);
}
{
m_finalFuncp = new AstCFunc(nodep->fileline(), "final", m_scopep);
m_finalFuncp->skipDecl(true);
m_finalFuncp->dontCombine(true);
m_finalFuncp->slow(true);
m_finalFuncp->addStmtsp(new AstCStmt(nodep->fileline(),
" "+v3Global.opt.prefix()+"__Syms::init(this);\n"));
m_scopep->addActivep(m_finalFuncp);
}
{
m_settleFuncp = new AstCFunc(nodep->fileline(), "_eval_settle", m_scopep);
m_settleFuncp->dontCombine(true);
m_settleFuncp->slow(true);
m_scopep->addActivep(m_settleFuncp);
}
// Process the activates
nodep->iterateChildren(*this);
// Done, clear so we can detect errors
UINFO(4," TOPSCOPEDONE "<<nodep<<endl);
clearLastSen();
m_topScopep=NULL;
m_scopep = NULL;
}
virtual void visit(AstModule* nodep, AstNUser*) {
//UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
m_stableNum = 0;
nodep->iterateChildren(*this);
}
virtual void visit(AstScope* nodep, AstNUser*) {
//UINFO(4," SCOPE "<<nodep<<endl);
m_scopep = nodep;
nodep->iterateChildren(*this);
if (AstNode* movep = nodep->finalClksp()) {
if (!m_topScopep) nodep->v3fatalSrc("Final clocks under non-top scope");
movep->unlinkFrBackWithNext();
m_evalFuncp->addFinalsp(movep);
}
m_scopep = NULL;
}
virtual void visit(AstAlways* nodep, AstNUser*) {
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
nodep->replaceWith(cmtp);
AstNode* stmtsp = nodep->bodysp();
if (stmtsp) {
stmtsp->unlinkFrBackWithNext();
cmtp->addNextHere(stmtsp);
}
nodep->deleteTree(); nodep = NULL;
}
virtual void visit(AstAlwaysPost* nodep, AstNUser*) {
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
nodep->replaceWith(cmtp);
AstNode* stmtsp = nodep->bodysp();
if (stmtsp) {
stmtsp->unlinkFrBackWithNext();
cmtp->addNextHere(stmtsp);
}
nodep->deleteTree(); nodep = NULL;
}
virtual void visit(AstInitial* nodep, AstNUser*) {
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName());
nodep->replaceWith(cmtp);
AstNode* stmtsp = nodep->bodysp();
if (stmtsp) {
stmtsp->unlinkFrBackWithNext();
cmtp->addNextHere(stmtsp);
}
nodep->deleteTree(); nodep = NULL;
}
void moveInitial(AstActive* nodep) {
// Change to CFunc
AstNode* stmtsp = nodep->stmtsp();
if (stmtsp) {
if (!m_scopep) nodep->v3fatalSrc("Initial Active not under scope\n");
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_initial__"+m_scopep->nameDotless(),
m_scopep);
funcp->slow(true);
stmtsp->unlinkFrBackWithNext();
funcp->addStmtsp(stmtsp);
nodep->replaceWith(funcp);
// Add top level call to it
m_initFuncp->addStmtsp(new AstCCall(nodep->fileline(), funcp));
} else {
nodep->unlinkFrBack();
}
nodep->deleteTree(); nodep=NULL;
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
nodep->iterateChildren(*this);
// Link to global function
if (nodep->formCallTree()) {
if (nodep->name() == "_final") {
UINFO(4, " formCallTree "<<nodep<<endl);
m_finalFuncp->addStmtsp(new AstCCall(nodep->fileline(), nodep));
} else {
nodep->v3fatalSrc("Unknown CFunc name. Make code more generic, with a map of func names");
}
}
}
virtual void visit(AstSenTree* nodep, AstNUser*) {
// Delete it later; Actives still pointing to it
nodep->unlinkFrBack();
pushDeletep(nodep);
}
void addToEvalLoop(AstNode* stmtsp) {
if (m_untilp) m_untilp->addBodysp(stmtsp); // In a until loop, add to body
else m_evalFuncp->addStmtsp(stmtsp); // else add to top level function
}
void addToSettleLoop(AstNode* stmtsp) {
if (m_untilp) m_untilp->addBodysp(stmtsp); // In a until loop, add to body
else m_settleFuncp->addStmtsp(stmtsp); // else add to top level function
}
virtual void visit(AstActive* nodep, AstNUser*) {
// Careful if adding variables here, ACTIVES can be under other ACTIVES
// Need to save and restore any member state in AstUntilStable block
if (nodep->hasInitial()) {
moveInitial(nodep);
}
else if (!m_topScopep || !nodep->stmtsp()) {
// Not at the top or empty block...
// Only empty blocks should be leftover on the non-top. Killem.
if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active");
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
} else {
UINFO(4," ACTIVE "<<nodep<<endl);
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
if (nodep->hasClocked()) {
// Remember the latest sensitivity so we can compare it next time
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
UINFO(4," sameSenseTree\n");
} else {
clearLastSen();
m_lastSenp = nodep->sensesp();
// Make a new if statement
m_lastIfp = makeActiveIf(m_lastSenp);
addToEvalLoop(m_lastIfp);
}
// Move statements to if
m_lastIfp->addIfsp(stmtsp);
} else if (nodep->hasSettle()) {
// Settlement
clearLastSen();
// Move statements to function
addToSettleLoop(stmtsp);
} else {
// Combo
clearLastSen();
// Move statements to function
addToEvalLoop(stmtsp);
}
nodep->unlinkFrBack()->deleteTree(); nodep = NULL;
}
}
#ifdef NEW_ORDERING
virtual void visit(AstUntilStable* nodep, AstNUser*) {
// Process any sub ACTIVE statements first
UINFO(4," UNTILSTABLE "<<nodep<<endl);
{
// Keep vars if in middle of other stable
AstUntilStable* lastUntilp = m_untilp;
AstSenTree* lastSenp = m_lastSenp;
AstIf* lastIfp = m_lastIfp;
m_untilp = nodep;
m_lastSenp = NULL;
m_lastIfp = NULL;
nodep->iterateChildren(*this);
m_untilp = lastUntilp;
m_lastSenp = lastSenp;
m_lastIfp = lastIfp;
}
// Set "unstable" to 100. (non-stabilization count)
// int __VclockLoop = 0
// IData __Vchange = 1
// while (__Vchange) {
// Save old values of each until stable variable
// Evaluate the body
// __Vchange = {change_detect} contribution
// if (++__VclockLoop > 100) converge_error
if (debug()>4) nodep->dumpTree(cout, " UntilSt-old: ");
FileLine* fl = nodep->fileline();
if (nodep->bodysp()) fl = nodep->bodysp()->fileline(); // Point to applicable code...
m_stableNum++;
AstNode* origBodysp = nodep->bodysp(); if (origBodysp) origBodysp->unlinkFrBackWithNext();
AstVarScope* changeVarp = getCreateLocalVar(fl, "__Vchange"+cvtToStr(m_stableNum), NULL, 32);
AstVarScope* countVarp = getCreateLocalVar(fl, "__VloopCount"+cvtToStr(m_stableNum), NULL, 32);
AstWhile* untilp = new AstWhile(fl, new AstVarRef(fl, changeVarp, false), NULL);
AstNode* preUntilp = new AstComment(fl, "Change loop "+cvtToStr(m_stableNum));
preUntilp->addNext(new AstAssign(fl, new AstVarRef(fl, changeVarp, true),
new AstConst(fl, 1)));
preUntilp->addNext(new AstAssign(fl, new AstVarRef(fl, countVarp, true),
new AstConst(fl, 0)));
// Add stable variables & preinits
AstNode* setChglastp = NULL;
for (AstVarRef* varrefp = nodep->stablesp(); varrefp; varrefp=varrefp->nextp()->castVarRef()) {
AstVarScope* cmpvscp = getCreateLocalVar(varrefp->varp()->fileline(),
"__Vchglast"+cvtToStr(m_stableNum)+"__"+varrefp->name(),
varrefp->varp(), 0);
varrefp->varScopep()->user2p(cmpvscp);
setChglastp = setChglastp->addNext(
new AstAssign(fl, new AstVarRef(fl, cmpvscp, true),
new AstVarRef(fl, varrefp->varScopep(), false)));
}
if (!setChglastp) nodep->v3fatalSrc("UntilStable without any variables");
untilp->addBodysp(setChglastp);
untilp->addBodysp(new AstComment(fl, "Change Loop body begin"));
if (origBodysp) untilp->addBodysp(origBodysp);
untilp->addBodysp(new AstComment(fl, "Change Loop body end"));
// Add stable checks
// The order of the variables doesn't matter, but it's more expensive to test
// wide variables, and more so 64 bit wide ones. Someday it might be faster to
// set the changed variable in a "distributed" fashion over the code, IE,
// logic... logic.... a=....; Changed |= (a ^ old_a); more logic... Changed |=...
// But then, one hopes users don't have much unoptimized logic
AstNode* changeExprp = NULL;
int doublecount = 0;
for (int wide=0; wide<3; wide++) { // Adding backwards; wide, quad, then normal
for (AstVarRef* varrefp = nodep->stablesp(); varrefp; varrefp=varrefp->nextp()->castVarRef()) {
if (wide== ( varrefp->isQuad()?0:(varrefp->isWide() ? 1:2))) {
AstVarScope* cmpvscp = (AstVarScope*)(varrefp->varScopep()->user2p());
if (!cmpvscp) varrefp->v3fatalSrc("should have created above");
AstChangeXor* changep
= new AstChangeXor (fl,
new AstVarRef(fl, varrefp->varScopep(), false),
new AstVarRef(fl, cmpvscp, false));
if (!changeExprp) changeExprp = changep;
else if (doublecount++ > DOUBLE_OR_RATE) {
doublecount = 0;
changeExprp = new AstLogOr (fl, changep, changeExprp);
} else {
changeExprp = new AstOr (fl, changep, changeExprp);
}
}
}
}
if (!changeExprp) nodep->v3fatalSrc("UntilStable without any variables");
changeExprp = new AstAssign(fl, new AstVarRef(fl, changeVarp, true),
changeExprp);
untilp->addBodysp(changeExprp);
//
// Final body
AstNode* ifstmtp = new AstAssign(fl, new AstVarRef(fl, countVarp, true),
new AstAdd(fl, new AstConst(fl, 1),
new AstVarRef(fl, countVarp, false)));
ifstmtp->addNext(new AstIf(fl,
new AstLt (fl, new AstConst(fl, 100),
new AstVarRef(fl, countVarp, false)),
(new AstDisplay (fl, '\n', "%%Error: Verilated model didn't converge", NULL, NULL))
->addNext(new AstStop (fl)),
NULL));
untilp->addBodysp(new AstIf(fl, new AstNeq(fl, new AstConst(fl, 0),
new AstVarRef(fl, changeVarp, false)),
ifstmtp, NULL));
//
// Replace it
preUntilp->addNext(untilp);
if (debug()>4) preUntilp->dumpTreeAndNext(cout, " UntilSt-new: ");
nodep->replaceWith(preUntilp); nodep->deleteTree(); nodep=NULL;
}
#endif
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
ClockVisitor(AstNode* nodep) {
m_modp=NULL; m_activep=NULL;
m_evalFuncp = NULL;
m_topScopep=NULL;
m_lastSenp=NULL;
m_lastIfp = NULL;
m_scopep = NULL;
m_stableNum = 0;
m_untilp = NULL;
//
nodep->accept(*this);
}
virtual ~ClockVisitor() {}
};
//######################################################################
// Clock class functions
void V3Clock::clockAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
ClockVisitor visitor (nodep);
}

35
src/V3Clock.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Clocking POS/NEGEDGE insertion
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3CLOCK_H_
#define _V3CLOCK_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Clock {
public:
static void clockAll(AstNetlist* nodep);
};
#endif // Guard

458
src/V3Combine.cpp Normal file
View File

@ -0,0 +1,458 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Combine common code into functions
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Combine's Transformations:
//
// For every function that we spit out
// Examine code to find largest common blocks
// Hash each node depth first
// Hash includes varp name and operator type, and constants
// Form lookup table based on hash of each statement w/ nodep and next nodep
// GO through table
// Lookup in hash, while next of each statement match, grow that common block
// Foreach common block
// If common block large enough (> 20 statements) & used 2x or more
// Make new function
// Move common block to function
// Replace each common block ref with funccall
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include <vector>
#include <map>
#include "V3Global.h"
#include "V3Combine.h"
#include "V3Hashed.h"
#include "V3Stats.h"
#include "V3Ast.h"
//######################################################################
#define COMBINE_MIN_STATEMENTS 50 // Min # of statements to be worth making a function
//######################################################################
class CombBaseVisitor : public AstNVisitor {
protected:
// STATE
//int debug() { return 9; }
// METHODS
virtual ~CombBaseVisitor() {}
//***** optimization levels
bool emptyFunctionDeletion() { return true; }
bool duplicateFunctionCombine() { return true; }
bool statementCombine() { return false && duplicateFunctionCombine(); }
};
//######################################################################
// Combine replacement function
class CombCallVisitor : CombBaseVisitor {
// Find all CCALLS of each CFUNC, so that we can later rename them
private:
// NODE STATE
bool m_find; // Find mode vs. delete mode
typedef multimap<AstCFunc*,AstCCall*> CallMmap;
CallMmap m_callMmap; // Associative array of {function}{call}
// METHODS
public:
void replaceFunc (AstCFunc* oldfuncp, AstCFunc* newfuncp) {
if (oldfuncp==newfuncp) return;
if (newfuncp) {
UINFO(4, " Replace "<<oldfuncp<<" -WITH-> "<<newfuncp<<endl);
} else UINFO(4, " Remove "<<oldfuncp<<endl);
pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(oldfuncp);
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
CallMmap::iterator eqit = nextit++;
AstCCall* callp = eqit->second;
if (!callp->user3()) { // !already done
UINFO(4, " Called "<<callp<<endl);
if (callp->funcp() != oldfuncp) callp->v3fatalSrc("Call list broken, points to call w/different func");
if (newfuncp) {
AstCCall* newp = new AstCCall(callp, newfuncp);
callp->replaceWith(newp);
addCall(newp); // Fix the table
} else { // Just deleting empty function
callp->unlinkFrBack();
}
callp->user3(true); // Dead now
pushDeletep(callp); callp=NULL;
m_callMmap.erase(eqit); // Fix the table
}
}
}
// METHODS
void addCall(AstCCall* nodep) {
m_callMmap.insert(make_pair(nodep->funcp(), nodep));
}
void deleteCall(AstCCall* nodep) {
pair <CallMmap::iterator,CallMmap::iterator> eqrange = m_callMmap.equal_range(nodep->funcp());
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
CallMmap::iterator eqit = nextit++;
AstCCall* callp = eqit->second;
if (callp==nodep) {
m_callMmap.erase(eqit);
return;
}
}
nodep->v3fatalSrc("deleteCall node not found in table");
}
private:
// VISITORS
virtual void visit(AstCCall* nodep, AstNUser*) {
addCall(nodep);
}
// Speed things up
virtual void visit(AstNodeAssign* nodep, AstNUser*) {}
virtual void visit(AstNodeMath* nodep, AstNUser*) {}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTRUCTORS
CombCallVisitor() {}
virtual ~CombCallVisitor() {}
void main(AstNetlist* nodep) {
nodep->accept(*this);
}
};
//######################################################################
// Combine marking function
class CombMarkVisitor : CombBaseVisitor {
// Mark all nodes under specified one.
private:
// OUTPUT:
// AstNode::user3() -> bool. True to indicate duplicated
// VISITORS
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->user3(true);
nodep->iterateChildren(*this);
}
public:
// CONSTRUCTORS
CombMarkVisitor(AstNode* nodep) {
nodep->accept(*this);
}
virtual ~CombMarkVisitor() {}
};
//######################################################################
// Combine state, as a visitor of each AstNode
class CombineVisitor : CombBaseVisitor {
private:
// NODE STATE
// Entire netlist:
// AstNodeStmt::user() -> bool. True if iterated already
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is illegal)
// STATE
typedef enum {STATE_IDLE, STATE_HASH, STATE_DUP} CombineState;
V3Double0 m_statCombs; // Statistic tracking
CombineState m_state; // Major state
AstModule* m_modp; // Current module
AstCFunc* m_funcp; // Current function
V3Hash m_lowerHash; // Hash of the statement we're building
CombCallVisitor m_call; // Tracking of function call users
int m_modNFuncs; // Number of functions made
AstNode* m_walkLast1p; // Final node that is the same in duplicate list
AstNode* m_walkLast2p; // Final node that is the same in duplicate list
V3Hashed m_hashed; // Hash for every node
// METHODS
void hashStatement(AstNode* nodep) {
// Compute hash on entire tree of this statement
m_hashed.hashAndInsert(nodep);
//UINFO(9," stmthash "<<hex<<nodep->user4()<<" "<<nodep<<endl);
}
void hashFunctions(AstCFunc* nodep) {
// Compute hash of all statement trees in the function
CombineState oldState = m_state;
{
m_state = STATE_HASH;
nodep->accept(*this);
}
m_state = oldState;
}
void walkEmptyFuncs() {
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
AstNode* node1p = it->second;
AstCFunc* oldfuncp = node1p->castCFunc();
if (oldfuncp
&& oldfuncp->emptyBody()
&& !oldfuncp->dontCombine()) {
UINFO(5," EmptyFunc "<<hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
// Mark user3p on entire old tree, so we don't process it more
CombMarkVisitor visitor(oldfuncp);
m_call.replaceFunc(oldfuncp, NULL);
oldfuncp->unlinkFrBack();
pushDeletep(oldfuncp); oldfuncp=NULL;
}
}
}
void walkDupFuncs() {
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
V3Hash hashval = it->first;
AstNode* node1p = it->second;
if (!node1p->castCFunc()) continue;
if (hashval.isIllegal()) node1p->v3fatalSrc("Illegal (unhashed) nodes\n");
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
AstNode* node2p = eqit->second;
if (!(eqit->first == hashval)) break;
if (node1p==node2p) continue; // Identical iterator
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
// Replace AstCCall's that point here
replaceFuncWFunc(node2p->castCFunc(), node1p->castCFunc());
// Replacement may promote a slow routine to fast path
if (!node2p->castCFunc()->slow()) node1p->castCFunc()->slow(false);
}
}
}
}
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
UINFO(5," DupFunc "<<hex<<V3Hash(newfuncp->user4p())<<" "<<newfuncp<<endl);
UINFO(5," and "<<hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
// Mark user3p on entire old tree, so we don't process it more
m_statCombs++;
CombMarkVisitor visitor(oldfuncp);
m_call.replaceFunc(oldfuncp, newfuncp);
oldfuncp->unlinkFrBack();
pushDeletep(oldfuncp); oldfuncp=NULL;
}
void replaceOnlyCallFunc(AstCCall* nodep) {
if (AstCFunc* oldfuncp = nodep->backp()->castCFunc()) {
//oldfuncp->dumpTree(cout,"MAYDEL: ");
if (nodep->nextp()==NULL
&& oldfuncp->initsp()==NULL
&& oldfuncp->stmtsp()==nodep
&& oldfuncp->finalsp()==NULL) {
UINFO(9," Function only has call "<<oldfuncp<<endl);
m_call.deleteCall(nodep);
CombMarkVisitor visitor(oldfuncp);
replaceFuncWFunc(oldfuncp, nodep->funcp()); nodep=NULL;
}
}
}
void walkDupCodeStart(AstNode* node1p) {
V3Hash hashval (node1p->user4p());
//UINFO(4," STMT "<<hashval<<" "<<node1p<<endl);
//
int bestDepth = 0; // Best substitution found in the search
AstNode* bestNode2p = NULL;
AstNode* bestLast1p = NULL;
AstNode* bestLast2p = NULL;
//
pair <V3Hashed::iterator,V3Hashed::iterator> eqrange = m_hashed.mmap().equal_range(hashval);
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
AstNode* node2p = eqit->second;
if (node1p==node2p) continue;
//
// We need to mark iteration to prevent matching code inside code (abab matching in ababab)
AstNode::userClearTree(); // userp() used on entire tree
m_walkLast1p = NULL;
m_walkLast2p = NULL;
int depth = walkDupCodeNext(node1p, node2p, 1);
if (depth>COMBINE_MIN_STATEMENTS
&& depth>bestDepth) {
bestDepth = depth;
bestNode2p = node2p;
bestLast1p = m_walkLast1p;
bestLast2p = m_walkLast2p;
}
}
if (bestDepth) {
// Found a replacement
UINFO(5," Duplicate of depth "<<bestDepth<<endl);
UINFO(5," DupFunc "<<" "<<node1p<<endl);
UINFO(5," and "<<" "<<bestNode2p<<endl);
UINFO(5," Through "<<" "<<bestLast1p<<endl);
UINFO(5," and "<<" "<<bestLast2p<<endl);
//
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
}
}
int walkDupCodeNext(AstNode* node1p, AstNode* node2p, int level) {
// Find number of common statements between the two node1p_nextp's...
if (node1p->userp() || node2p->userp()) return 0; // Already iterated
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
if (!m_hashed.sameNodes(node1p,node2p)) return 0; // walk of tree has same comparison
V3Hash hashval(node1p->user4p());
//UINFO(9," wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
//UINFO(9," wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
m_walkLast1p = node1p;
m_walkLast2p = node2p;
node1p->user(true);
node2p->user(true);
if (node1p->nextp() && node2p->nextp()) {
return hashval.depth()+walkDupCodeNext(node1p->nextp(), node2p->nextp(), level+1);
}
return hashval.depth();
}
void walkReplace(AstNode* node1p, AstNode* node2p,
AstNode* last1p, AstNode* last2p) { // Final node in linked list, maybe null if all statements to be grabbed
// Make new function
string oldname = m_funcp->name();
string::size_type pos;
if ((pos=oldname.find("_common")) != string::npos) {
oldname.erase(pos);
}
if ((pos=oldname.find("__")) != string::npos) {
oldname.erase(pos);
}
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
oldname+"_common"+cvtToStr(++m_modNFuncs),
NULL);
m_modp->addStmtp(newfuncp);
// Create calls
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp);
// Grab statement bodies
AstNRelinker relink1Handle;
AstNRelinker relink2Handle;
for (AstNode* nextp, *walkp = node1p; 1; walkp = nextp) {
nextp = walkp->nextp();
if (walkp==node1p) walkp->unlinkFrBack(&relink1Handle);
else { walkp->unlinkFrBack(); node1p->addNext(walkp); }
if (walkp==last1p) break;
}
for (AstNode* nextp, *walkp = node2p; 1; walkp = nextp) {
nextp = walkp->nextp();
if (walkp==node2p) walkp->unlinkFrBack(&relink2Handle);
else { walkp->unlinkFrBack(); node2p->addNext(walkp); }
if (walkp==last2p) break;
}
// Move node1 statements to new function
newfuncp->addStmtsp(node1p);
//newfuncp->dumpTree(cout," newfunctree: ");
// Mark node2 statements as dead
CombMarkVisitor visitor(node2p);
pushDeletep(node2p); // Delete later
// Link in new function
relink1Handle.relink(call1p);
relink2Handle.relink(call2p);
// Hash the new function
hashFunctions(newfuncp);
m_call.addCall(call1p);
m_call.addCall(call2p);
// If either new statement makes a func with only a single call, replace
// the above callers to call it directly
replaceOnlyCallFunc(call1p); call1p=NULL;
replaceOnlyCallFunc(call2p); call2p=NULL;
}
// VISITORS
virtual void visit(AstNetlist* nodep, AstNUser*) {
// Track all callers of each function
m_call.main(nodep);
//
AstNode::user3ClearTree(); // userp() used on entire tree
//In V3Hashed AstNode::user4ClearTree(); // userp() used on entire tree
// Iterate modules backwards, in bottom-up order.
// Required so that a module instantiating another can benefit from collapsing.
nodep->iterateChildrenBackwards(*this);
}
virtual void visit(AstModule* nodep, AstNUser*) {
UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
m_modNFuncs = 0;
m_hashed.clear();
// Compute hash of all statement trees in the function
m_state = STATE_HASH;
nodep->iterateChildren(*this);
m_state = STATE_IDLE;
// Walk the hashes removing empty functions
if (emptyFunctionDeletion()) {
walkEmptyFuncs();
}
// Walk the hashes looking for duplicate functions
if (duplicateFunctionCombine()) {
walkDupFuncs();
}
// Walk the statements looking for large replicated code sections
if (statementCombine()) {
m_state = STATE_DUP;
nodep->iterateChildren(*this);
m_state = STATE_IDLE;
}
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
m_funcp = nodep;
if (!nodep->dontCombine()) {
if (m_state == STATE_HASH) {
hashStatement(nodep); // Hash the entire function - it might be identical
} else if (m_state == STATE_DUP) {
nodep->iterateChildren(*this);
}
}
m_funcp = NULL;
}
virtual void visit(AstNodeStmt* nodep, AstNUser*) {
if (m_state == STATE_HASH && m_funcp) {
hashStatement(nodep);
}
else if (m_state == STATE_DUP && m_funcp) {
walkDupCodeStart(nodep);
}
}
//--------------------
// Default: Just iterate
virtual void visit(AstVar*, AstNUser*) {}
virtual void visit(AstTraceDecl*, AstNUser*) {}
virtual void visit(AstTraceInc*, AstNUser*) {}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
CombineVisitor(AstNetlist* nodep) {
m_modp=NULL;
m_funcp = NULL;
m_state = STATE_IDLE;
nodep->accept(*this);
}
virtual ~CombineVisitor() {
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
}
};
//######################################################################
// Combine class functions
void V3Combine::combineAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
CombineVisitor visitor (nodep);
}

35
src/V3Combine.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Combine common code into functions
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3COMBINE_H_
#define _V3COMBINE_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Combine {
public:
static void combineAll(AstNetlist* nodep);
};
#endif // Guard

1152
src/V3Const.cpp Normal file

File diff suppressed because it is too large Load Diff

44
src/V3Const.h Normal file
View File

@ -0,0 +1,44 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Propagate constants across AST
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3CONST_H_
#define _V3CONST_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Const {
public:
// Force this cell node's parameter list to become a constant
static void constifyParam(AstNode* nodep);
// Everything that's possible
static void constifyAll(AstNetlist* nodep);
// Also, warn
static void constifyAllLint(AstNode* nodep);
// C++ datatypes
static void constifyCpp(AstNode* nodep);
// Only the current node and lower
static void constifyTree(AstNode* nodep);
};
#endif // Guard

164
src/V3Coverage.cpp Normal file
View File

@ -0,0 +1,164 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Netlist (top level) functions
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// COVERAGE TRANSFORMATIONS:
// At each IF/(IF else)/CASEITEM,
// If there's no coverage off on the block below it,
// or a $stop
// Insert a COVERDECL node in the module.
// (V3Emit reencodes into per-module numbers for emitting.)
// Insert a COVERINC node at the end of the statement list
// for that if/else/case.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <map>
#include "V3Global.h"
#include "V3Coverage.h"
#include "V3Ast.h"
#include "V3Const.h"
//######################################################################
// Coverage state, as a visitor of each AstNode
class CoverageVisitor : public AstNVisitor {
private:
// TYPES
typedef map<FileLine*,int> FileMap;
// STATE
bool m_checkBlock; // Should this block get covered?
AstModule* m_modp; // Current module to add statement to
FileMap m_fileps; // Column counts for each fileline
//int debug() { return 9; }
// METHODS
AstCoverInc* newCoverInc(FileLine* fl, const string& type, const string& comment) {
int column = 0;
FileMap::iterator it = m_fileps.find(fl);
if (it == m_fileps.end()) {
m_fileps.insert(make_pair(fl,column+1));
} else {
column = (it->second)++;
}
AstCoverDecl* declp = new AstCoverDecl(fl, column, type, comment);
m_modp->addStmtp(declp);
return new AstCoverInc(fl, declp);
}
// VISITORS
virtual void visit(AstModule* nodep, AstNUser*) {
m_modp = nodep;
m_fileps.clear();
nodep->iterateChildren(*this);
m_modp = NULL;
}
virtual void visit(AstIf* nodep, AstNUser*) {
UINFO(4," IF: "<<nodep<<endl);
if (m_checkBlock) {
nodep->ifsp()->iterateAndNext(*this);
if (m_checkBlock && v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it
if (!nodep->backp()->castIf()
|| nodep->backp()->castIf()->elsesp()!=nodep) { // Ignore if else; did earlier
UINFO(4," COVER: "<<nodep<<endl);
nodep->addIfsp(newCoverInc(nodep->fileline(), "block", "if"));
}
}
// Don't do empty else's, only empty if/case's
if (nodep->elsesp()) {
m_checkBlock = true;
nodep->elsesp()->iterateAndNext(*this);
if (m_checkBlock && v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it
UINFO(4," COVER: "<<nodep<<endl);
if (nodep->elsesp()->castIf()) {
nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(), "block", "elsif"));
} else {
nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(), "block", "else"));
}
}
}
m_checkBlock = true; // Reset as a child may have cleared it
}
}
virtual void visit(AstCaseItem* nodep, AstNUser*) {
UINFO(4," CASEI: "<<nodep<<endl);
if (m_checkBlock && v3Global.opt.coverageLine()) {
nodep->bodysp()->iterateAndNext(*this);
if (m_checkBlock) { // if the case body didn't disable it
UINFO(4," COVER: "<<nodep<<endl);
nodep->addBodysp(newCoverInc(nodep->fileline(), "block", "case"));
}
m_checkBlock = true; // Reset as a child may have cleared it
}
}
virtual void visit(AstPslCover* nodep, AstNUser*) {
UINFO(4," PSLCOVER: "<<nodep<<endl);
nodep->iterateChildren(*this);
if (!nodep->coverincp()) {
// Note the name may be overridden by V3Assert processing
nodep->coverincp(newCoverInc(nodep->fileline(), "psl_cover", "cover"));
}
}
virtual void visit(AstStop* nodep, AstNUser*) {
UINFO(4," STOP: "<<nodep<<endl);
m_checkBlock = false;
}
virtual void visit(AstPragma* nodep, AstNUser*) {
if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) {
// Skip all NEXT nodes under this block, and skip this if/case branch
UINFO(4," OFF: "<<nodep<<endl);
m_checkBlock = false;
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
if (m_checkBlock) nodep->iterateChildren(*this);
}
virtual void visit(AstNode* nodep, AstNUser*) {
// Default: Just iterate
if (m_checkBlock) {
nodep->iterateChildren(*this);
m_checkBlock = true; // Reset as a child may have cleared it
}
}
public:
// CONSTUCTORS
CoverageVisitor(AstNetlist* rootp) {
// Operate on all modules
m_checkBlock = true;
rootp->iterateChildren(*this);
}
virtual ~CoverageVisitor() {}
};
//######################################################################
// Coverage class functions
void V3Coverage::coverage(AstNetlist* rootp) {
UINFO(2,__FUNCTION__<<": "<<endl);
CoverageVisitor visitor (rootp);
}

36
src/V3Coverage.h Normal file
View File

@ -0,0 +1,36 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Coverage modules/signals together
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3COVERAGE_H_
#define _V3COVERAGE_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Coverage {
public:
// CREATORS
static void coverage(AstNetlist* rootp);
};
#endif // Guard

148
src/V3Dead.cpp Normal file
View File

@ -0,0 +1,148 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Dead code elimination
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// DEAD TRANSFORMATIONS:
// Remove any unreferenced modules
// Remove any unreferenced variables
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <vector>
#include "V3Global.h"
#include "V3Dead.h"
#include "V3Ast.h"
//######################################################################
// Dead state, as a visitor of each AstNode
class DeadVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire Netlist:
// AstModule::user() -> int. Count of number of cells referencing this module.
// AstVar::user() -> int. Count of number of references
// AstVarScope::user() -> int. Count of number of references
// STATE
vector<AstVar*> m_varsp; // List of all encountered to avoid another loop through three
vector<AstVarScope*> m_vscsp; // List of all encountered to avoid another loop through three
bool m_elimUserVars; // Allow removal of user's vars
//int debug() { return 9; }
// METHODS
// VISITORS
virtual void visit(AstCell* nodep, AstNUser*) {
nodep->iterateChildren(*this);
nodep->modp()->user(nodep->modp()->user() + 1);
}
virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->varScopep()) {
nodep->varScopep()->user(nodep->varScopep()->user() + 1);
nodep->varScopep()->varp()->user(nodep->varScopep()->varp()->user() + 1);
}
if (nodep->varp()) {
nodep->varp()->user(nodep->varp()->user() + 1);
}
}
virtual void visit(AstVarScope* nodep, AstNUser*) {
nodep->iterateChildren(*this);
m_vscsp.push_back(nodep);
}
virtual void visit(AstVar* nodep, AstNUser*) {
nodep->iterateChildren(*this);
m_varsp.push_back(nodep);
}
//-----
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
// METHODS
void deadCheckMod() {
// Kill any unused modules
for (bool retry=true; retry; ) {
retry=false;
AstModule* nextmodp;
for (AstModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
nextmodp = modp->nextp()->castModule();
if (modp->level()>2 && modp->user()==0) {
// > 2 because L1 is the wrapper, L2 is the top user module
UINFO(4," Dead module "<<modp<<endl);
// And its children may now be killable too....
for (AstNode* nodep = modp->stmtsp(); nodep; nodep=nodep->nextp()) {
if (AstCell* cellp=nodep->castCell()) {
cellp->modp()->user( cellp->modp()->user() - 1);
retry = true;
}
}
modp->unlinkFrBack()->deleteTree(); modp=NULL;
}
}
}
}
bool canElim(AstVar* nodep) {
return (!nodep->isSigPublic() // Can't elim publics!
&& (nodep->isTemp() || nodep->isParam() || m_elimUserVars));
}
void deadCheckVar() {
// Delete any unused varscopes
for (vector<AstVarScope*>::iterator it = m_vscsp.begin(); it!=m_vscsp.end(); ++it) {
if ((*it)->user() == 0 && canElim((*it)->varp())) {
UINFO(4," Dead "<<(*it)<<endl);
(*it)->unlinkFrBack()->deleteTree(); (*it)=NULL;
}
}
for (vector<AstVar*>::iterator it = m_varsp.begin(); it!=m_varsp.end(); ++it) {
if ((*it)->user() == 0 && canElim((*it))) {
UINFO(4," Dead "<<(*it)<<endl);
(*it)->unlinkFrBack()->deleteTree(); (*it)=NULL;
}
}
}
public:
// CONSTRUCTORS
DeadVisitor(AstNetlist* nodep, bool elimUserVars) {
m_elimUserVars = elimUserVars;
// Operate on whole netlist
AstNode::userClearTree(); // userp() used on entire tree
nodep->accept(*this);
deadCheckVar();
// Modules after vars, because might be vars we delete inside a mod we delete
deadCheckMod();
}
virtual ~DeadVisitor() {}
};
//######################################################################
// Dead class functions
void V3Dead::deadifyAll(AstNetlist* nodep, bool elimUserVars) {
UINFO(2,__FUNCTION__<<": "<<endl);
DeadVisitor visitor (nodep, elimUserVars);
}

36
src/V3Dead.h Normal file
View File

@ -0,0 +1,36 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Dead branch elimination
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3DEAD_H_
#define _V3DEAD_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Dead {
public:
// Everything that's possible
static void deadifyAll(AstNetlist* nodep, bool elimUserVars);
};
#endif // Guard

459
src/V3Delayed.cpp Normal file
View File

@ -0,0 +1,459 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Add temporaries, such as for delayed nodes
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Delayed's Transformations:
//
// Each module:
// Replace ASSIGNDLY var, exp
// With ASSIGNDLY newvar, exp
// At top of block: VAR newvar
// At bottom of block: ASSIGNW var newvar
// Need _x_dly = x at top of active if "x" is not always set
// For now we'll say it's set if at top of block (not under IF, etc)
// Need x = _x_dly at bottom of active if "x" is never referenced on LHS
// in the active, and above rule applies too. (If so, use x on LHS, not _x_dly.)
//
// If a signal is set in multiple always blocks, we need a dly read & set with
// multiple clock sensitivities. We have 3 options:
// 1. When detected, make a new ACTIVE and move earlier created delayed assignment there
// 2. Form unique ACTIVE for every multiple clocked assignment
// 3. Predetect signals from multiple always blocks and do #2 on them
// Since all 3 require a top activation cleanup, we do #2 which is easiest.
//
// ASSIGNDLY (BITSEL(ARRAYSEL (VARREF(v), bits), selbits), rhs)
// -> VAR __Vdlyvset_x
// VAR __Vdlyvval_x
// VAR __Vdlyvdim_x
// VAR __Vdlyvlsb_x
// ASSIGNW (__Vdlyvset_x,0)
// ...
// ASSIGNW (VARREF(__Vdlyvval_x), rhs)
// ASSIGNW (__Vdlyvdim_x, dimension_number)
// ASSIGNW (__Vdlyvset_x, 1)
// ...
// ASSIGNW (BITSEL(ARRAYSEL(VARREF(x), __Vdlyvdim_x), __Vdlyvlsb_x), __Vdlyvval_x)
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include <map>
#include <deque>
#include "V3Global.h"
#include "V3Delayed.h"
#include "V3Ast.h"
#include "V3Stats.h"
//######################################################################
// Delayed state, as a visitor of each AstNode
class DelayedVisitor : public AstNVisitor {
private:
// NODE STATE
// Cleared each module:
// AstVarScope::userp() -> AstVarScope*. Points to temp var created.
// AstVarScope::user2p() -> AstActive*. Points to activity block of signal
// AstVarScope::user4p() -> AstAlwaysPost*. Post block for this variable
// AstVar::user2() -> bool. Set true if already made warning
// AstVar::user3() -> VarUsage. Tracks delayed vs non-delayed usage
// AstVar::user4() -> int. Vector number, for assignment creation
// AstVarRef::user2() -> bool. Set true if already processed
// AstAlwaysPost::user4() -> AstIf*. Last IF (__Vdlyvset__) created under this AlwaysPost
// Cleared each scope:
// AstAssignDly::user5() -> AstVarScope*. __Vdlyvset__ created for this assign
// AstAlwaysPost::user5() -> AstVarScope*. __Vdlyvset__ last referenced in IF
enum VarUsage { VU_NONE=0, VU_DLY=1, VU_NONDLY=2 };
// STATE
AstActive* m_activep; // Current activate
AstCFunc* m_cfuncp; // Current public C Function
AstAssignDly* m_nextDlyp; // Next delayed assignment in a list of assignments
bool m_inDly; // True in delayed assignments
bool m_inLoop; // True in for loops
bool m_inInitial; // True in intial blocks
typedef std::map<pair<AstModule*,string>,AstVar*> VarMap;
VarMap m_modVarMap; // Table of new var names created under module
V3Double0 m_statSharedSet;// Statistic tracking
//static int debug() { return 9; }
// METHODS
void markVarUsage(AstVar* nodep, uint32_t flags) {
//UINFO(4," MVU "<<flags<<" "<<nodep<<endl);
nodep->user3( nodep->user3() | flags );
if ((nodep->user3() & VU_DLY) && (nodep->user3() & VU_NONDLY)) {
nodep->v3warn(BLKANDNBLK,"Unsupported: Blocked and non-blocking assignments to same variable: "<<nodep->prettyName());
}
}
AstVarScope* createVarSc(AstVarScope* oldvarscp, string name, int width/*0==fromoldvar*/) {
// Because we've already scoped it, we may need to add both the AstVar and the AstVarScope
AstRange* rangep = NULL;
if (width==0) {
rangep = new AstRange(oldvarscp->fileline(),
oldvarscp->varp()->msb(),
oldvarscp->varp()->lsb());
} else if (width==1) {
rangep = NULL;
} else {
rangep = new AstRange(oldvarscp->fileline(),
width-1, 0);
}
if (!oldvarscp->scopep()) oldvarscp->v3fatalSrc("Var unscoped");
AstVar* varp;
AstModule* 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()) {
// Created module's AstVar earlier under some other scope
varp = iter->second;
} else {
varp = new AstVar (oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, rangep);
if (width==0) varp->widthSignedFrom(oldvarscp);
addmodp->addStmtp(varp);
m_modVarMap.insert(make_pair(make_pair(addmodp, name), varp));
}
AstVarScope* varscp = new AstVarScope (oldvarscp->fileline(), oldvarscp->scopep(), varp);
oldvarscp->scopep()->addVarp(varscp);
return varscp;
}
AstNode* createDlyArray(AstAssignDly* nodep, AstNode* lhsp) {
// Create delayed assignment
// See top of this file for transformation
// Return the new LHS for the assignment, Null = unlink
// Find selects
AstNode* newlhsp = NULL; // NULL = unlink old assign
AstSel* bitselp = NULL;
AstArraySel* arrayselp = NULL;
if (lhsp->castSel()) {
bitselp = lhsp->castSel();
arrayselp = bitselp->fromp()->castArraySel();
} else {
arrayselp = lhsp->castArraySel();
}
if (!arrayselp) nodep->v3fatalSrc("No arraysel under bitsel?");
UINFO(4,"AssignDlyArray: "<<nodep<<endl);
//
//=== Dimensions: __Vdlyvdim__
deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment
AstNode* dimselp=arrayselp;
for (; dimselp->castArraySel(); dimselp=dimselp->castArraySel()->fromp()) {
AstNode* valp = dimselp->castArraySel()->bitp()->unlinkFrBack();
dimvalp.push_front(valp);
}
AstVarRef* varrefp = dimselp->castVarRef();
if (!varrefp) nodep->v3fatalSrc("No var underneath arraysels\n");
if (!varrefp->varScopep()) varrefp->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp\n");
varrefp->unlinkFrBack();
AstVar* oldvarp = varrefp->varp();
int modVecNum = oldvarp->user4(); oldvarp->user4(modVecNum+1);
//
deque<AstNode*> dimreadps; // Read value for each dimension of assignment
for (unsigned dimension=0; dimension<dimvalp.size(); dimension++) {
AstNode* dimp = dimvalp[dimension];
if (dimp->castConst()) { // bit = const, can just use it
dimreadps.push_front(dimp);
} else {
string bitvarname = (string("__Vdlyvdim")+cvtToStr(dimension)
+"__"+oldvarp->shortName()+"__"+cvtToStr(modVecNum));
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, dimp->width());
AstAssign* bitassignp
= new AstAssign (nodep->fileline(),
new AstVarRef(nodep->fileline(), bitvscp, true),
dimp);
nodep->addNextHere(bitassignp);
dimreadps.push_front(new AstVarRef(nodep->fileline(), bitvscp, false));
}
}
//
//=== Bitselect: __Vdlyvlsb__
AstNode* bitreadp=NULL; // Code to read Vdlyvlsb
if (bitselp) {
AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack();
if (bitselp->fromp()->castConst()) {// vlsb = constant, can just push constant into where we use it
bitreadp = lsbvaluep;
} else {
string bitvarname = (string("__Vdlyvlsb__")+oldvarp->shortName()+"__"+cvtToStr(modVecNum));
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width());
AstAssign* bitassignp = new AstAssign (nodep->fileline(),
new AstVarRef(nodep->fileline(), bitvscp, true),
lsbvaluep);
nodep->addNextHere(bitassignp);
bitreadp = new AstVarRef(nodep->fileline(), bitvscp, false);
}
}
//
//=== Value: __Vdlyvval__
AstNode* valreadp; // Code to read Vdlyvval
if (nodep->rhsp()->castConst()) { // vval = constant, can just push constant into where we use it
valreadp = nodep->rhsp()->unlinkFrBack();
} else {
string valvarname = (string("__Vdlyvval__")+oldvarp->shortName()+"__"+cvtToStr(modVecNum));
AstVarScope* valvscp = createVarSc(varrefp->varScopep(), valvarname, nodep->rhsp()->width());
newlhsp = new AstVarRef(nodep->fileline(), valvscp, true);
valreadp = new AstVarRef(nodep->fileline(), valvscp, false);
}
//
//=== Setting/not setting boolean: __Vdlyvset__
bool sharedVset = false;
AstVarScope* setvscp;
if (nodep->user5p()) {
// Simplistic optimization. If the previous statement in same scope was also a =>,
// then we told this nodep->user5 we can use its Vdlyvset rather then making a new one.
// This is good for code like:
// for (i=0; i<5; i++) vector[i] <= something;
sharedVset = true;
setvscp = nodep->user5p()->castNode()->castVarScope();
m_statSharedSet++;
} else { // Create new one
string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__"+cvtToStr(modVecNum));
setvscp = createVarSc(varrefp->varScopep(), setvarname, 1);
AstAssignPre* setinitp
= new AstAssignPre (nodep->fileline(),
new AstVarRef(nodep->fileline(), setvscp, true),
new AstConst(nodep->fileline(), 0));
m_activep->addStmtsp(setinitp);
AstAssign* setassignp
= new AstAssign (nodep->fileline(),
new AstVarRef(nodep->fileline(), setvscp, true),
new AstConst(nodep->fileline(),
V3Number(nodep->fileline(),1,true)));
nodep->addNextHere(setassignp);
}
if (m_nextDlyp) { // Tell next assigndly it can share the variable
m_nextDlyp->user5p(setvscp);
}
//
// Create ALWAYSPOST for delayed variable
// We add all logic to the same block if it's for the same memory
// This insures that multiple assignments to the same memory will result
// in correctly ordered code - the last assignment must be last.
// It also has the nice side effect of assisting cache locality.
AstNode* selectsp = varrefp;
for (int dimension=int(dimreadps.size())-1; dimension>=0; --dimension) {
selectsp = new AstArraySel(nodep->fileline(), selectsp, dimreadps[dimension]);
}
if (bitselp) {
selectsp = new AstSel(nodep->fileline(), selectsp, bitreadp,
bitselp->widthp()->cloneTree(false));
}
// Build "IF (changeit) ...
UINFO(9," For "<<setvscp<<endl);
UINFO(9," & "<<varrefp<<endl);
AstAlwaysPost* finalp = varrefp->varScopep()->user4p()->castNode()->castAlwaysPost();
if (!finalp) {
finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/);
UINFO(9," Created "<<finalp<<endl);
m_activep->addStmtsp(finalp);
varrefp->varScopep()->user4p(finalp);
}
AstIf* postLogicp;
if (finalp->user5p()->castNode() == setvscp) {
// Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*,
// we can share the IF statement too
postLogicp = finalp->user4p()->castNode()->castIf();
if (!postLogicp) nodep->v3fatalSrc("Delayed assignment misoptimized; prev var found w/o associated IF");
} else {
postLogicp = new AstIf (nodep->fileline(),
new AstVarRef(nodep->fileline(), setvscp, false),
NULL,
NULL);
UINFO(9," Created "<<postLogicp<<endl);
finalp->addBodysp(postLogicp);
finalp->user5p(setvscp); // Remember IF's vset variable
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
}
postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp));
return newlhsp;
}
// VISITORS
virtual void visit(AstNetlist* nodep, AstNUser*) {
//VV***** We reset all userp() on the netlist
m_modVarMap.clear();
AstNode::userClearTree();
AstNode::user2ClearTree();
AstNode::user3ClearTree();
AstNode::user4ClearTree();
nodep->iterateChildren(*this);
}
virtual void visit(AstScope* nodep, AstNUser*) {
UINFO(4," MOD "<<nodep<<endl);
AstNode::user5ClearTree();
nodep->iterateChildren(*this);
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
m_cfuncp = nodep;
nodep->iterateChildren(*this);
m_cfuncp = NULL;
}
virtual void visit(AstActive* nodep, AstNUser*) {
m_activep = nodep;
bool oldinit = m_inInitial;
m_inInitial = nodep->hasInitial();
nodep->iterateChildren(*this);
m_inInitial = oldinit;
}
virtual void visit(AstAssignDly* nodep, AstNUser*) {
m_inDly = true;
m_nextDlyp = nodep->nextp()->castAssignDly(); // Next assignment in same block, maybe NULL.
if (m_cfuncp) nodep->v3error("Unsupported: Delayed assignment inside public function/task");
if (nodep->lhsp()->castArraySel()
|| (nodep->lhsp()->castSel()
&& nodep->lhsp()->castSel()->fromp()->castArraySel())) {
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newlhsp = createDlyArray(nodep, lhsp);
if (m_inLoop) nodep->v3error("Unsupported: Delayed assignment to array inside for loops (non-delayed is ok - see docs)");
if (newlhsp) {
nodep->lhsp(newlhsp);
} else {
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
lhsp->deleteTree(); lhsp=NULL;
}
else {
nodep->iterateChildren(*this);
}
m_inDly = false;
m_nextDlyp = NULL;
}
virtual void visit(AstVarRef* nodep, AstNUser*) {
if (!nodep->user2()) { // Not done yet
nodep->user2(true);
if (m_inDly && nodep->lvalue()) {
UINFO(4,"AssignDlyVar: "<<nodep<<endl);
markVarUsage(nodep->varp(), VU_DLY);
if (!m_activep) nodep->v3fatalSrc("<= not under sensitivity block");
if (!m_activep->hasClocked()) nodep->v3error("Internal: Blocking <= assignment in non-clocked block, should have converted in V3Active");
AstVarScope* oldvscp = nodep->varScopep();
if (!oldvscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp\n");
AstVarScope* dlyvscp = oldvscp->userp()->castNode()->castVarScope();
if (dlyvscp) { // Multiple use of delayed variable
AstActive* oldactivep = dlyvscp->user2p()->castNode()->castActive();
if (!oldactivep) nodep->v3fatalSrc("<= old dly assignment not put under sensitivity block");
if (oldactivep->sensesp() != m_activep->sensesp()) {
if (!nodep->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN)
&& !nodep->varp()->user2()) {
nodep->varp()->v3warn(MULTIDRIVEN,"Signal has multiple driving blocks: "<<nodep->varp()->prettyName());
nodep->v3warn(MULTIDRIVEN,"... Location of first driving block");
oldactivep->v3warn(MULTIDRIVEN,"... Location of other driving block");
nodep->varp()->user2(true);
}
UINFO(4,"AssignDupDlyVar: "<<nodep<<endl);
UINFO(4," Act: "<<m_activep<<endl);
UINFO(4," Act: "<<oldactivep<<endl);
// Make a new sensitivity list, which is the combination of both blocks
AstSenItem* sena = m_activep->sensesp()->sensesp()->cloneTree(true)->castSenItem();
AstSenItem* senb = oldactivep->sensesp()->sensesp()->cloneTree(true)->castSenItem();
AstSenTree* treep = new AstSenTree(m_activep->fileline(), sena);
if (senb) treep->addSensesp(senb);
if (AstSenTree* storep = oldactivep->sensesStorep()) {
storep->unlinkFrBack();
pushDeletep(storep);
}
oldactivep->sensesStorep(treep);
oldactivep->sensesp(treep);
}
}
if (!dlyvscp) { // First use of this delayed variable
string newvarname = (string("__Vdly__")+nodep->varp()->shortName());
dlyvscp = createVarSc(oldvscp, newvarname, 0);
AstNodeAssign* prep
= new AstAssignPre (nodep->fileline(),
new AstVarRef(nodep->fileline(), dlyvscp, true),
new AstVarRef(nodep->fileline(), oldvscp, false));
AstNodeAssign* postp
= new AstAssignPost (nodep->fileline(),
new AstVarRef(nodep->fileline(), oldvscp, true),
new AstVarRef(nodep->fileline(), dlyvscp, false));
postp->lhsp()->user2(true); // Don't detect this assignment
oldvscp->userp(dlyvscp); // So we can find it later
// Make new ACTIVE with identical sensitivity tree
AstActive* newactp = new AstActive (nodep->fileline(), "sequentdly",
m_activep->sensesp());
newactp->addStmtsp(prep); // Add to FRONT of statements
newactp->addStmtsp(postp);
m_activep->addNext(newactp);
dlyvscp->user2p(newactp);
}
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), dlyvscp, true);
newrefp->user2(true); // No reason to do it again
nodep->replaceWith(newrefp); nodep->deleteTree(); nodep=NULL;
}
else if (!m_inDly && nodep->lvalue()) {
//UINFO(9,"NBA "<<nodep<<endl);
if (!m_inInitial) {
markVarUsage(nodep->varp(), VU_NONDLY);
}
}
}
}
virtual void visit(AstNodeFor* nodep, AstNUser*) {
nodep->v3fatalSrc("For statements should have been converted to while statements in V3Unroll\n");
}
virtual void visit(AstWhile* nodep, AstNUser*) {
bool oldloop = m_inLoop;
m_inLoop = true;
nodep->iterateChildren(*this);
m_inLoop = oldloop;
}
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
DelayedVisitor(AstNode* nodep) {
m_inDly = false;
m_activep=NULL;
m_cfuncp=NULL;
m_nextDlyp=NULL;
m_inLoop = false;
m_inInitial = false;
nodep->accept(*this);
}
virtual ~DelayedVisitor() {
V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet);
}
};
//######################################################################
// Delayed class functions
void V3Delayed::delayedAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
DelayedVisitor visitor (nodep);
}

35
src/V3Delayed.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Pre C-Emit stage changes
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3DELAYED_H_
#define _V3DELAYED_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Delayed {
public:
static void delayedAll(AstNetlist* nodep);
};
#endif // Guard

155
src/V3Depth.cpp Normal file
View File

@ -0,0 +1,155 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Prevent very deep expressions
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Depth's Transformations:
//
// Each module:
// For each wide OP, assign a temporary variable.
// For each deep expression, assign expression to temporary.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include "V3Global.h"
#include "V3Depth.h"
#include "V3Ast.h"
//######################################################################
// Depth state, as a visitor of each AstNode
class DepthVisitor : public AstNVisitor {
private:
// NODE STATE
// STATE
AstModule* m_modp; // Current module
AstCFunc* m_funcp; // Current block
AstNode* m_stmtp; // Current statement
int m_depth; // How deep in an expression
int m_maxdepth; // Maximum depth in an expression
//int debug() { return 9; }
// MSVC++ has a limit of 100 parenthesis. We have some operator
// defines that use 2 parens. Thus we can't have a expression deeper
// then 50 operators. We'll add some margin though.
enum en { MAX_EXPR_DEPTH = 40 }; // Expressions deeper then this need temp
// METHODS
void createDeepTemp(AstNode* nodep) {
UINFO(6," Deep "<<nodep<<endl);
//if (debug()>=9) nodep->dumpTree(cout,"deep:");
string newvarname = ((string)"__Vdeeptemp__"+cvtToStr(m_modp->varNumGetInc()));
AstVar* varp = new AstVar (nodep->fileline(), AstVarType::STMTTEMP, newvarname,
// Width, not widthMin, as we may be in middle of BITSEL expression which
// though it's one bit wide, needs the mask in the upper bits.
// (Someday we'll have a valid bitmask instead of widths....)
new AstRange(nodep->fileline(), nodep->width()-1, 0));
m_funcp->addInitsp(varp);
// Replace node tree with reference to var
AstVarRef* newp = new AstVarRef (nodep->fileline(), varp, false);
nodep->replaceWith(newp);
// Put assignment before the referencing statement
AstAssign* assp = new AstAssign (nodep->fileline(),
new AstVarRef(nodep->fileline(), varp, true),
nodep);
AstNRelinker linker2;
m_stmtp->unlinkFrBack(&linker2);
assp->addNext(m_stmtp);
linker2.relink(assp);
}
// VISITORS
virtual void visit(AstModule* nodep, AstNUser*) {
UINFO(4," MOD "<<nodep<<endl);
m_modp = nodep;
m_funcp = NULL;
nodep->iterateChildren(*this);
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
m_funcp = nodep;
m_depth = 0;
m_maxdepth = 0;
nodep->iterateChildren(*this);
}
virtual void visit(AstNodeStmt* nodep, AstNUser*) {
m_depth = 0;
m_maxdepth = 0;
m_stmtp = nodep;
nodep->iterateChildren(*this);
m_stmtp = NULL;
}
// Operators
virtual void visit(AstNodeTermop* nodep, AstNUser*) {
}
virtual void visit(AstNodeMath* nodep, AstNUser*) {
m_depth++;
if (m_depth>m_maxdepth) m_maxdepth=m_depth;
nodep->iterateChildren(*this);
m_depth--;
if ((m_maxdepth-m_depth) > MAX_EXPR_DEPTH
&& m_stmtp
&& !nodep->backp()->castNodeStmt() // Not much point if we're about to use it
) {
m_maxdepth = m_depth;
createDeepTemp(nodep);
}
}
//--------------------
// Default: Just iterate
virtual void visit(AstVar* nodep, AstNUser*) {} // Don't hit varrefs under vars
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
DepthVisitor(AstNode* nodep) {
m_modp=NULL;
m_funcp=NULL;
m_stmtp=NULL;
m_depth=0;
m_maxdepth=0;
//
AstNode::userClearTree(); // userp() used on entire tree
nodep->accept(*this);
}
virtual ~DepthVisitor() {}
};
//----------------------------------------------------------------------
// Top loop
//######################################################################
// Depth class functions
void V3Depth::depthAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
// We must do it in bottom-up module
DepthVisitor visitor (nodep);
}

35
src/V3Depth.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Prevent very deep expressions
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3DEPTH_H_
#define _V3DEPTH_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Depth {
public:
static void depthAll(AstNetlist* nodep);
};
#endif // Guard

252
src/V3Descope.cpp Normal file
View File

@ -0,0 +1,252 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Rename scope references to module-local references
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// DESCOPE TRANSFORMATIONS:
// All modules:
// Each VARREF/FUNCCALL
// Change varref name() to be relative to current module
// Remove varScopep()
// This allows for better V3Combine'ing.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <map>
#include "V3Global.h"
#include "V3Descope.h"
#include "V3Ast.h"
//######################################################################
class DescopeVisitor : public AstNVisitor {
private:
// NODE STATE
// Cleared entire netlist
// AstCFunc::user() // bool. Indicates processing completed
// TYPES
typedef multimap<string,AstCFunc*> FuncMmap;
// STATE
AstModule* m_modp; // Current module
AstScope* m_scopep; // Current scope
bool m_needThis; // Add thisp to function
FuncMmap m_modFuncs; // Name of public functions added
//int debug() { return 9; }
// METHODS
string descopedName(AstScope* scopep, bool& hierThisr, AstVar* varp=NULL) {
UASSERT(scopep, "Var/Func not scoped\n");
hierThisr = true;
if (varp && varp->isFuncLocal()) {
return ""; // Relative to function, not in this
} else if (scopep == m_scopep) {
if (m_modp->isTop()) {
return ""; // Reference to scope we're in, no need to HIER-> it
} else {
m_needThis = true;
return "thisp->"; // this-> but with restricted aliasing
}
} else if (scopep->aboveScopep() && scopep->aboveScopep()==m_scopep
&& 0 // DISABLED: GCC considers the pointers ambiguous, so goes ld/store crazy
) {
// Reference to scope of cell directly under this module, can just "cell->"
string name = scopep->name();
string::size_type pos;
if ((pos = name.rfind(".")) != string::npos) {
name.erase(0,pos+1);
}
hierThisr = false;
return name+"->";
} else {
// Reference to something else, use global variable
m_modp->globalSyms(true);
UINFO(8," Descope "<<scopep<<endl);
UINFO(8," to "<<scopep->name()<<endl);
UINFO(8," under "<<m_scopep->name()<<endl);
hierThisr = false;
if (!scopep->aboveScopep()) { // Top
return "VlSym->TOPp->";
} else {
return scopep->nameVlSym()+".";
}
}
}
void makePublicFuncWrappers() {
// We recorded all public functions in m_modFuncs.
// If for any given name only one function exists, we can use that function directly.
// If multiple functions exist, we need to select the appropriate scope.
for (FuncMmap::iterator it = m_modFuncs.begin(); it!=m_modFuncs.end(); ++it) {
string name = it->first;
AstCFunc* topFuncp = it->second;
FuncMmap::iterator nextIt1 = it; ++nextIt1;
bool moreOfSame1 = (nextIt1!=m_modFuncs.end() && nextIt1->first == name);
if (moreOfSame1) {
// Multiple functions under this name, need a wrapper function
UINFO(6," Wrapping "<<name<<" multifuncs\n");
AstCFunc* newfuncp = topFuncp->cloneTree(false)->castCFunc();
if (newfuncp->initsp()) newfuncp->initsp()->unlinkFrBackWithNext()->deleteTree();
if (newfuncp->stmtsp()) newfuncp->stmtsp()->unlinkFrBackWithNext()->deleteTree();
if (newfuncp->finalsp()) newfuncp->finalsp()->unlinkFrBackWithNext()->deleteTree();
newfuncp->name(name);
topFuncp->addNextHere(newfuncp);
// In the body, call each function if it matches the given scope
for (FuncMmap::iterator eachIt = it; eachIt!=m_modFuncs.end() && eachIt->first==name; ++eachIt) {
it = eachIt;
AstCFunc* funcp = eachIt->second;
FuncMmap::iterator nextIt2 = eachIt; ++nextIt2;
bool moreOfSame = (nextIt2!=m_modFuncs.end() && nextIt2->first == name);
if (!funcp->scopep()) funcp->v3fatalSrc("Not scoped");
UINFO(6," Wrapping "<<name<<" "<<funcp<<endl);
UINFO(6," at "<<newfuncp->argTypes()<<" und "<<funcp->argTypes()<<endl);
funcp->declPrivate(true);
AstNode* argsp = NULL;
for (AstNode* stmtp = newfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) {
if (AstVar* portp = stmtp->castVar()) {
if (portp->isIO() && !portp->isFuncReturn()) {
argsp = argsp->addNextNull(new AstVarRef(portp->fileline(), portp,
portp->isOutput()));
}
}
}
AstNode* returnp = new AstCReturn (funcp->fileline(),
new AstCCall (funcp->fileline(),
funcp,
argsp));
if (moreOfSame) {
AstIf* ifp = new AstIf (funcp->fileline(),
new AstEq(funcp->fileline(),
new AstCMath(funcp->fileline(),
"this", 64),
new AstCMath(funcp->fileline(),
string("&(")
+funcp->scopep()->nameVlSym()
+")", 64)),
returnp, NULL);
newfuncp->addStmtsp(ifp);
} else {
newfuncp->addStmtsp(returnp);
}
}
// Not really any way the user could do this, and we'd need to come up with some return value
//newfuncp->addStmtsp(new AstDisplay (newfuncp->fileline(),
// '\n', string("%%Error: ")+name+"() called with bad scope", NULL));
//newfuncp->addStmtsp(new AstStop (newfuncp->fileline()));
if (debug()>=9) newfuncp->dumpTree(cout," newfunc: ");
} else {
// Only a single function under this name, we can simply rename it
UINFO(6," Wrapping "<<name<<" just one "<<topFuncp<<endl);
topFuncp->name(name);
}
}
}
// VISITORS
virtual void visit(AstModule* nodep, AstNUser*) {
m_modp = nodep;
m_modFuncs.clear();
nodep->iterateChildren(*this);
makePublicFuncWrappers();
}
virtual void visit(AstScope* nodep, AstNUser*) {
m_scopep = nodep;
nodep->iterateChildren(*this);
m_scopep = NULL;
}
virtual void visit(AstVarScope* nodep, AstNUser*) {
// Delete the varscope when we're finished
nodep->unlinkFrBack();
pushDeletep(nodep);
}
virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
nodep->iterateChildren(*this);
// Convert the hierch name
if (!m_scopep) nodep->v3fatalSrc("Node not under scope");
bool hierThis;
nodep->hiername(descopedName(nodep->varScopep()->scopep(), hierThis/*ref*/, nodep->varScopep()->varp()));
nodep->hierThis(hierThis);
nodep->varScopep(NULL);
}
virtual void visit(AstCCall* nodep, AstNUser*) {
//UINFO(9," "<<nodep<<endl);
nodep->iterateChildren(*this);
// Convert the hierch name
if (!m_scopep) nodep->v3fatalSrc("Node not under scope");
if (!nodep->funcp()->scopep()) nodep->v3fatalSrc("CFunc not under scope");
bool hierThis;
nodep->hiername(descopedName(nodep->funcp()->scopep(), hierThis/*ref*/));
// Can't do this, as we may have more calls later
// nodep->funcp()->scopep(NULL);
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
if (!nodep->user()) {
m_needThis = false;
nodep->iterateChildren(*this);
nodep->user(true);
if (m_needThis) {
// Really we should have more node types for backend optimization of this stuff
string text = " "+v3Global.opt.modPrefix() + "_" + m_modp->name()
+"* thisp = &("+m_scopep->nameVlSym()+");\n";
nodep->addInitsp(new AstCStmt(nodep->fileline(), text));
}
// If it's under a scope, move it up to the top
if (m_scopep) {
nodep->unlinkFrBack();
m_modp->addStmtp(nodep);
}
if (nodep->funcPublic()) {
// There may be multiple public functions by the same name;
// record for later correction or making of shells
m_modFuncs.insert(make_pair(nodep->name(), nodep));
nodep->name(m_scopep->nameDotless() +"__" + nodep->name());
}
}
}
virtual void visit(AstVar*, AstNUser*) {}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTRUCTORS
DescopeVisitor(AstNetlist* nodep) {
m_modp = NULL;
m_scopep = NULL;
m_needThis = false;
AstNode::userClearTree();
nodep->accept(*this);
}
virtual ~DescopeVisitor() {}
};
//######################################################################
// Descope class functions
void V3Descope::descopeAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
DescopeVisitor visitor (nodep);
}

35
src/V3Descope.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Rename scope references to module-local references
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3DESCOPE_H_
#define _V3DESCOPE_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Descope {
public:
static void descopeAll(AstNetlist* nodep);
};
#endif // Guard

1855
src/V3EmitC.cpp Normal file

File diff suppressed because it is too large Load Diff

37
src/V3EmitC.h Normal file
View File

@ -0,0 +1,37 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Emit C++ code for module tree
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3EMITC_H_
#define _V3EMITC_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3EmitC {
public:
static void emitc();
static void emitcSyms();
static void emitcTrace();
};
#endif // Guard

158
src/V3EmitCBase.h Normal file
View File

@ -0,0 +1,158 @@
// $Id$ -*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Emit C++ for tree
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3EMITCBASE_H_
#define _V3EMITCBASE_H_ 1
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <math.h>
#include "V3Global.h"
#include "V3File.h"
#include "V3Ast.h"
//######################################################################
// V3OutCFile: A class for abstracting out SystemC/C++ details
class V3OutCFile : public V3OutFile {
int m_private;
public:
V3OutCFile(const string& filename) : V3OutFile(filename) {
resetPrivate();
}
virtual ~V3OutCFile() {};
virtual void putsCellDecl(const string& classname, const string& cellname) {
this->printf("%-19s\t%s;\n",
(classname + "*").c_str(),cellname.c_str());
}
virtual void putsHeader() { puts("// Verilated -*- C++ -*-\n"); }
virtual void putsIntTopInclude() { }
// Print out public/privates
void resetPrivate() { m_private = 0; }
void putsPrivate(bool setPrivate) {
if (setPrivate && m_private!=1) {
puts("private:\n");
m_private = 1;
} else if (!setPrivate && m_private!=2) {
puts("public:\n");
m_private = 2;
}
}
};
class V3OutScFile : public V3OutCFile {
public:
V3OutScFile(const string& filename) : V3OutCFile(filename) {}
virtual ~V3OutScFile() {};
virtual void putsHeader() { puts("// Verilated -*- SystemC -*-\n"); }
virtual void putsIntTopInclude() { puts("#include \"systemc.h\"\n"); }
};
class V3OutSpFile : public V3OutCFile {
public:
V3OutSpFile(const string& filename) : V3OutCFile(filename) {}
virtual ~V3OutSpFile() {};
virtual void putsHeader() { puts("// Verilated -*- SystemC -*-\n"); }
virtual void putsIntTopInclude() { puts("#include \"systemperl.h\"\n"); }
};
class V3OutVFile : public V3OutCFile {
public:
V3OutVFile(const string& filename) : V3OutCFile(filename) {}
virtual ~V3OutVFile() {};
virtual void putsHeader() { puts("// Verilated -*- Verilog -*-\n"); }
};
class V3OutMkFile : public V3OutFile {
public:
V3OutMkFile(const string& filename) : V3OutFile(filename) {}
virtual ~V3OutMkFile() {};
virtual void putsHeader() { puts("# Verilated -*- Makefile -*-\n"); }
// No automatic indentation yet.
void puts(const char* strg) { putsNoTracking(strg); }
void puts(const string& strg) { putsNoTracking(strg); }
};
//######################################################################
// Base Visitor class -- holds output file pointer
class EmitCBaseVisitor : public AstNVisitor {
public:
// STATE
V3OutCFile* m_ofp;
// METHODS
V3OutCFile* ofp() const { return m_ofp; };
void puts(const string& str) { ofp()->puts(str); }
void putbs(const string& str) { ofp()->putbs(str); }
bool optSystemC() { return v3Global.opt.systemC(); }
bool optSystemPerl() { return v3Global.opt.systemPerl(); }
static string symClassName() { return v3Global.opt.prefix()+"__Syms"; }
static string modClassName(AstModule* modp) { // Return name of current module being processed
if (modp->isTop()) {
return v3Global.opt.prefix();
} else {
return v3Global.opt.modPrefix() + "_" + modp->name();
}
}
static string topClassName() { // Return name of top wrapper module
return v3Global.opt.prefix();
}
AstCFile* newCFile(const string& filename, bool slow, bool source) {
AstCFile* cfilep = new AstCFile(v3Global.rootp()->fileline(), filename);
cfilep->slow(slow);
cfilep->source(source);
v3Global.rootp()->addFilesp(cfilep);
return cfilep;
}
// CONSTRUCTORS
EmitCBaseVisitor() {
m_ofp = NULL;
}
virtual ~EmitCBaseVisitor() {}
};
//######################################################################
// Fileize state, as a visitor of each AstNode
class EmitCBaseCounterVisitor : public AstNVisitor {
private:
// STATE
int m_count; // Number of statements
// VISITORS
virtual void visit(AstNode* nodep, AstNUser*) {
m_count++;
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
EmitCBaseCounterVisitor(AstNode* nodep) {
m_count = 0;
nodep->accept(*this);
}
virtual ~EmitCBaseCounterVisitor() {}
int count() const { return m_count; }
};
#endif // guard

228
src/V3EmitCSyms.cpp Normal file
View File

@ -0,0 +1,228 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Emit C++ for tree
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <math.h>
#include <map>
#include <set>
#include <vector>
#include <algorithm>
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3EmitCBase.h"
#include "V3LanguageWords.h"
//######################################################################
// Symbol table emitting
class EmitCSyms : EmitCBaseVisitor {
// STATE
AstModule* m_modp; // Current module
typedef pair<AstScope*,AstModule*> ScopeModPair;
vector<ScopeModPair> m_scopes; // Every scope by module
V3LanguageWords m_words; // Reserved word detector
// METHODS
void emitInt();
void emitImp();
struct CmpName {
inline bool operator () (const ScopeModPair& lhsp, const ScopeModPair& rhsp) const {
return lhsp.first->name() < rhsp.first->name();
}
};
void nameCheck(AstNode* nodep) {
// Prevent GCC compile time error; name check all things that reach C++ code
if (nodep->name() != "") {
string rsvd = m_words.isKeyword(nodep->name());
if (rsvd != "") {
nodep->v3error("Unsupported: "+rsvd+": "<<nodep->name());
}
}
}
// VISITORS
virtual void visit(AstNetlist* nodep, AstNUser*) {
// Collect list of scopes
nodep->iterateChildren(*this);
// Sort m_scopes by scope name
sort(m_scopes.begin(), m_scopes.end(), CmpName());
// Output
emitInt();
emitImp();
}
virtual void visit(AstModule* nodep, AstNUser*) {
nameCheck(nodep);
m_modp = nodep;
nodep->iterateChildren(*this);
}
virtual void visit(AstScope* nodep, AstNUser*) {
nameCheck(nodep);
m_scopes.push_back(make_pair(nodep, m_modp));
}
// NOPs
virtual void visit(AstNodeStmt*, AstNUser*) {}
virtual void visit(AstConst*, AstNUser*) {}
// Default
virtual void visit(AstNode* nodep, AstNUser*) {
nameCheck(nodep);
nodep->iterateChildren(*this);
}
//---------------------------------------
// ACCESSORS
public:
EmitCSyms(AstNetlist* nodep) {
m_modp = NULL;
nodep->accept(*this);
}
};
void EmitCSyms::emitInt() {
string filename = v3Global.opt.makeDir()+"/"+symClassName()+".h";
newCFile(filename, true/*slow*/, false/*source*/);
V3OutCFile hf (filename);
m_ofp = &hf;
ofp()->putsHeader();
puts("#ifndef _"+symClassName()+"_H_\n");
puts("#define _"+symClassName()+"_H_\n");
puts("\n");
// for
puts("\n// INCLUDE MODULE CLASSES\n");
for (AstModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castModule()) {
puts("#include \""+modClassName(nodep)+".h\"\n");
}
puts("\n// SYMS CLASS\n");
puts((string)"class "+symClassName()+" {\n");
puts("public:\n");
puts("// STATIC STATE\n");
puts("static "+symClassName()+"* s_thisp;\n");
puts("\n// STATE\n");
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
AstScope* scopep = it->first; AstModule* modp = it->second;
if (modp->isTop()) {
ofp()->printf("%-30s ", (modClassName(modp)+"*").c_str());
puts(scopep->nameDotless()+"p;\n");
}
else {
ofp()->printf("%-30s ", (modClassName(modp)+"").c_str());
puts(scopep->nameDotless()+";\n");
}
}
puts("\n// CREATORS\n");
puts(symClassName()+"("+topClassName()+"* topp);\n");
puts((string)"~"+symClassName()+"() {};\n");
puts("// METHODS\n");
puts("/// Called at top of each eval to setup global pointer to top-of-symbol table\n");
puts((string)"inline static void init ("+symClassName()+"* symsp) {\n");
puts("s_thisp = symsp;\n");
puts("}\n");
puts((string)"inline static void init ("+topClassName()+"* topp) {\n");
puts("s_thisp = topp->__VlSymsp;\n");
puts("}\n");
puts("\n");
puts("};\n");
puts("#endif /*guard*/\n");
}
void EmitCSyms::emitImp() {
string filename = v3Global.opt.makeDir()+"/"+symClassName()+".cpp";
AstCFile* cfilep = newCFile(filename, true/*slow*/, true/*source*/);
cfilep->support(true);
V3OutCFile cf (filename);
m_ofp = &cf;
ofp()->putsHeader();
puts("\n");
// Includes
puts("#include \""+symClassName()+".h\"\n");
for (AstModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castModule()) {
puts("#include \""+modClassName(nodep)+".h\"\n");
}
puts("\n// GLOBALS\n");
puts(symClassName()+"* "+symClassName()+"::s_thisp;\n");
puts("\n// FUNCTIONS\n");
puts(symClassName()+"::"+symClassName()+"("+topClassName()+"* topp)\n");
puts("\t// Setup submodule names\n");
char comma=':';
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
AstScope* scopep = it->first; AstModule* modp = it->second;
if (modp->isTop()) {
} else {
string arrow = scopep->name();
if (arrow.substr(0,4) == "TOP.") arrow.replace(0,4,".");
ofp()->printf("\t%c %-30s ", comma, scopep->nameDotless().c_str());
puts("(Verilated::catName(topp->name(),\"");
puts(arrow);
puts("\"))\n");
comma=',';
}
}
puts("{\n");
puts("// Pointer to top level\n");
puts("TOPp = topp;\n");
puts("// Setup each module's pointers to their submodules\n");
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
AstScope* scopep = it->first; AstModule* modp = it->second;
if (!modp->isTop()) {
string arrow = scopep->name();
string::size_type pos;
while ((pos=arrow.find(".")) != string::npos) {
arrow.replace(pos, 1, "->");
}
if (arrow.substr(0,5) == "TOP->") arrow.replace(0,5,"TOPp->");
ofp()->printf("%-30s ", arrow.c_str());
puts(" = &");
puts(scopep->nameDotless()+";\n");
}
}
puts("// Setup each module's pointer back to symbol table (for public functions)\n");
puts("TOPp->__VlSymsp = this;\n");
for (vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) {
AstScope* scopep = it->first; AstModule* modp = it->second;
if (!modp->isTop()) {
puts(scopep->nameDotless()+".__Vconfigure(this);\n");
}
}
puts("}\n");
puts("\n");
}
//######################################################################
// EmitC class functions
void V3EmitC::emitcSyms() {
UINFO(2,__FUNCTION__<<": "<<endl);
EmitCSyms syms (v3Global.rootp());
}

164
src/V3EmitMk.cpp Normal file
View File

@ -0,0 +1,164 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Emit Makefile
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2004-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <math.h>
#include <map>
#include <vector>
#include <algorithm>
#include "V3Global.h"
#include "V3EmitMk.h"
#include "V3EmitCBase.h"
//######################################################################
// Emit statements and math operators
class EmitMkVisitor : public EmitCBaseVisitor {
public:
//int debug() { return 9; }
// METHODS
void emitClassMake() {
// Generate the makefile
V3OutMkFile of (v3Global.opt.makeDir()+"/"+ v3Global.opt.prefix() + "_classes.mk");
of.putsHeader();
of.puts("\n");
of.puts("VM_TRACE = "); of.puts(v3Global.opt.trace()?"1":"0"); of.puts("\n");
of.puts("\n");
for (int support=0; support<2; support++) {
for (int slow=0; slow<2; slow++) {
of.puts(support?"VM_SUPPORT":"VM_CLASSES");
of.puts(slow?"_SLOW":"_FAST");
of.puts(" += \\\n");
for (AstCFile* nodep = v3Global.rootp()->filesp(); nodep; nodep=nodep->nextp()->castCFile()) {
if (nodep->source() && nodep->slow()==slow && nodep->support()==support) {
of.puts("\t"+V3Options::filenameNonDirExt(nodep->name())+" \\\n");
}
}
of.puts("\n");
}
}
of.puts("\n");
of.putsHeader();
}
void emitOverallMake() {
// Generate the makefile
V3OutMkFile of (v3Global.opt.makeDir()+"/"+ v3Global.opt.prefix() + ".mk");
of.putsHeader();
of.puts("\n");
if (v3Global.opt.exe()) {
of.puts("default: "+v3Global.opt.prefix()+"\n");
} else {
of.puts("default: "+v3Global.opt.prefix()+"__ALL.a\n");
}
of.puts("\n# Constants...\n");
of.puts("PERL = "+V3Options::getenvStr("PERL","perl")+"\n");
of.puts("VERILATOR_ROOT = "+V3Options::getenvStr("VERILATOR_ROOT","")+"\n");
of.puts("SYSTEMPERL = "+V3Options::getenvStr("SYSTEMPERL","")+"\n");
of.puts("\n# Switches...\n");
of.puts(string("VM_SP = ")+(v3Global.opt.systemPerl()?"1":"0")+"\n");
of.puts(string("VM_SC = ")+((v3Global.opt.systemC()&&!v3Global.opt.systemPerl())?"1":"0")+"\n");
of.puts(string("VM_SP_OR_SC = ")+(v3Global.opt.systemC()?"1":"0")+"\n");
of.puts(string("VM_PCLI = ")+(v3Global.opt.systemC()?"0":"1")+"\n");
of.puts(string("VM_SC_TARGET_ARCH = ")+V3Options::getenvStr("SYSTEMC_ARCH","")+"\n");
of.puts("\n# Vars...\n");
of.puts(string("VM_PREFIX = ")+v3Global.opt.prefix()+"\n");
of.puts(string("VM_MODPREFIX = ")+v3Global.opt.modPrefix()+"\n");
// Imply generating verilated.o
if (v3Global.opt.exe()) {
v3Global.opt.addCppFile("verilated.cpp");
}
V3StringSet dirs;
of.puts("VM_USER_CLASSES = \\\n");
for (V3StringSet::iterator it = v3Global.opt.cppFiles().begin();
it != v3Global.opt.cppFiles().end(); ++it) {
string cppfile = *it;
of.puts("\t"+V3Options::filenameNonExt(cppfile)+" \\\n");
string dir = V3Options::filenameDir(cppfile);
if (dirs.find(dir) == dirs.end()) dirs.insert(dir);
}
of.puts("\n");
of.puts("VM_USER_DIR = \\\n");
for (V3StringSet::iterator it = dirs.begin(); it!=dirs.end(); ++it) {
of.puts("\t"+*it+" \\\n");
}
of.puts("\n");
of.puts("\n# Default rules...\n");
of.puts("include "+v3Global.opt.prefix()+"_classes.mk\n");
of.puts("include $(VERILATOR_ROOT)/include/verilated.mk\n");
of.puts("\n# Local rules...\n");
if (v3Global.opt.exe()) {
of.puts("VPATH += $(VM_USER_DIR)\n");
of.puts("\n");
for (V3StringSet::iterator it = v3Global.opt.cppFiles().begin();
it != v3Global.opt.cppFiles().end(); ++it) {
string cppfile = *it;
string basename = V3Options::filenameNonExt(cppfile);
of.puts(basename+".o: "+cppfile+"\n");
of.puts("\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $<\n");
}
of.puts("\n# Link rules...\n");
of.puts(v3Global.opt.prefix()+": $(VK_USER_OBJS) $(SP_SRCS) $(VM_PREFIX)__ALL.a\n");
of.puts("\t$(CXX) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ $(LIBS) 2>&1 | c++filt\n");
of.puts("\n");
}
of.puts("\n");
of.putsHeader();
}
//--------------------
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->v3fatalSrc("No visitors implemented.");
}
public:
EmitMkVisitor(AstNetlist*) {
emitClassMake();
emitOverallMake();
}
virtual ~EmitMkVisitor() {}
};
//######################################################################
// Gate class functions
void V3EmitMk::emitmk(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
EmitMkVisitor visitor (nodep);
}

35
src/V3EmitMk.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Emit Makefile
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3EMITMK_H_
#define _V3EMITMK_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3EmitMk {
public:
static void emitmk(AstNetlist* nodep);
};
#endif // Guard

443
src/V3EmitV.cpp Normal file
View File

@ -0,0 +1,443 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Emit Verilog from tree
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2004-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <math.h>
#include <map>
#include <vector>
#include <algorithm>
#include "V3Global.h"
#include "V3EmitV.h"
#include "V3EmitCBase.h"
//######################################################################
// Emit statements and math operators
class EmitVImp : public EmitCBaseVisitor {
private:
bool m_suppressSemi;
AstModule* m_modp;
public:
//int debug() { return 9; }
// METHODS
// VISITORS
virtual void visit(AstNetlist* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
virtual void visit(AstModule* nodep, AstNUser*) {
m_modp = nodep;
putbs("module "+modClassName(nodep)+";\n");
nodep->iterateChildren(*this);
puts("endmodule\n");
}
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
putbs(nodep->castTask() ? "task ":"function ");
puts(nodep->name());
puts(";\n");
putbs("begin\n");
nodep->stmtsp()->iterateAndNext(*this);
puts("end\n");
}
virtual void visit(AstBegin* nodep, AstNUser*) {
putbs("begin\n");
nodep->iterateChildren(*this);
puts("end\n");
}
virtual void visit(AstGenerate* nodep, AstNUser*) {
putbs("generate\n");
nodep->iterateChildren(*this);
puts("end\n");
}
virtual void visit(AstFinal* nodep, AstNUser*) {
putbs("final begin\n");
nodep->iterateChildren(*this);
puts("end\n");
}
virtual void visit(AstInitial* nodep, AstNUser*) {
putbs("initial begin\n");
nodep->iterateChildren(*this);
puts("end\n");
}
virtual void visit(AstAlways* nodep, AstNUser*) {
putbs("always ");
nodep->sensesp()->iterateAndNext(*this);
putbs("begin\n");
nodep->bodysp()->iterateAndNext(*this);
puts("end\n");
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
nodep->lhsp()->iterateAndNext(*this);
putbs(" "+nodep->verilogKwd()+" ");
nodep->rhsp()->iterateAndNext(*this);
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstAssignDly* nodep, AstNUser*) {
nodep->lhsp()->iterateAndNext(*this);
putbs(" <= ");
nodep->rhsp()->iterateAndNext(*this);
puts(";\n");
}
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
putbs("assign ");
nodep->lhsp()->iterateAndNext(*this);
putbs(" = ");
nodep->rhsp()->iterateAndNext(*this);
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstAssignW* nodep, AstNUser*) {
putbs("assign ");
nodep->lhsp()->iterateAndNext(*this);
putbs(" = ");
nodep->rhsp()->iterateAndNext(*this);
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstSenTree* nodep, AstNUser*) {
putbs("@(");
nodep->iterateChildren(*this);
puts(") ");
}
virtual void visit(AstSenItem* nodep, AstNUser*) {
putbs("");
if (nodep->backp()->castSenItem()) puts(" or ");
puts(nodep->edgeType().verilogKwd());
puts(" ");
nodep->iterateChildren(*this);
puts(" ");
}
virtual void visit(AstNodeCase* nodep, AstNUser*) {
putbs(nodep->verilogKwd());
puts(" (");
nodep->exprp()->iterateAndNext(*this);
puts(")\n");
if (AstCase* casep = nodep->castCase()) {
if (casep->fullPragma() || casep->parallelPragma()) {
puts(" // synopsys");
if (casep->fullPragma()) puts(" full_case");
if (casep->parallelPragma()) puts(" parallel_case");
}
}
nodep->itemsp()->iterateAndNext(*this);
putbs("endcase");
puts("\n");
}
virtual void visit(AstCaseItem* nodep, AstNUser*) {
if (nodep->condsp()) {
nodep->condsp()->iterateAndNext(*this);
} else putbs("default");
putbs(": begin ");
nodep->bodysp()->iterateAndNext(*this);
puts("end\n");
}
virtual void visit(AstComment* nodep, AstNUser*) {
puts((string)"// "+nodep->name()+"\n");
nodep->iterateChildren(*this);
}
virtual void visit(AstCoverDecl*, AstNUser*) {
// N/A
}
virtual void visit(AstCoverInc*, AstNUser*) {
// N/A
}
virtual void visit(AstDisplay* nodep, AstNUser*) {
putbs(nodep->verilogKwd());
putbs(" (");
if (nodep->filep()) { nodep->filep()->iterateAndNext(*this); putbs(","); }
puts("\"");
ofp()->putsNoTracking(nodep->text());
puts("\"");
for (AstNode* expp=nodep->exprsp(); expp; expp = expp->nextp()) {
puts(",");
expp->iterateAndNext(*this);
}
puts(");\n");
}
virtual void visit(AstFOpen* nodep, AstNUser*) {
putbs(nodep->verilogKwd());
putbs(" (");
if (nodep->filep()) nodep->filep()->iterateChildren(*this);
putbs(",");
if (nodep->filenamep()) nodep->filenamep()->iterateChildren(*this);
putbs(",");
if (nodep->modep()) nodep->modep()->iterateChildren(*this);
puts(");\n");
}
virtual void visit(AstFClose* nodep, AstNUser*) {
putbs(nodep->verilogKwd());
putbs(" (");
if (nodep->filep()) nodep->filep()->iterateChildren(*this);
puts(");\n");
}
virtual void visit(AstNodeFor* nodep, AstNUser*) {
puts("for (");
m_suppressSemi = true;
nodep->initsp()->iterateAndNext(*this);
puts(";");
nodep->condp()->iterateAndNext(*this);
puts(";");
nodep->assignsp()->iterateAndNext(*this);
m_suppressSemi = false;
puts(") {\n");
nodep->bodysp()->iterateAndNext(*this);
puts("}\n");
}
virtual void visit(AstWhile* nodep, AstNUser*) {
nodep->precondsp()->iterateAndNext(*this);
puts("while (");
nodep->condp()->iterateAndNext(*this);
puts(") {\n");
nodep->bodysp()->iterateAndNext(*this);
nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop
puts("}\n");
}
virtual void visit(AstNodeIf* nodep, AstNUser*) {
puts("if (");
nodep->condp()->iterateAndNext(*this);
puts(") begin\n");
nodep->ifsp()->iterateAndNext(*this);
if (nodep->elsesp()) {
puts("end\n");
puts("else begin\n");
nodep->elsesp()->iterateAndNext(*this);
}
puts("end\n");
}
virtual void visit(AstStop*, AstNUser*) {
putbs("$stop;\n");
}
virtual void visit(AstFinish*, AstNUser*) {
putbs("$finish;\n");
}
virtual void visit(AstText* nodep, AstNUser*) {
ofp()->putsNoTracking(nodep->text());
}
virtual void visit(AstCStmt* nodep, AstNUser*) {
putbs("$_CSTMT(");
nodep->bodysp()->iterateAndNext(*this);
puts(");\n");
}
virtual void visit(AstCMath* nodep, AstNUser*) {
putbs("$_CMATH(");
nodep->bodysp()->iterateAndNext(*this);
puts(");\n");
}
virtual void visit(AstUCStmt* nodep, AstNUser*) {
putbs("$c(");
nodep->bodysp()->iterateAndNext(*this);
puts(");\n");
}
virtual void visit(AstUCFunc* nodep, AstNUser*) {
putbs("$c(");
nodep->bodysp()->iterateAndNext(*this); puts(")\n");
}
// Operators
virtual void emitVerilogFormat(AstNode* nodep, const string& format,
AstNode* lhsp=NULL, AstNode* rhsp=NULL, AstNode* thsp=NULL) {
// Look at emitVerilog() format for term/uni/dual/triops,
// and write out appropriate text.
// %l lhsp - if appropriate
// %r rhsp - if appropriate
// %t thsp - if appropriate
// %k Potential line break
bool inPct = false;
putbs("");
for (string::const_iterator pos = format.begin(); pos != format.end(); ++pos) {
if (pos[0]=='%') {
inPct = true;
} else if (!inPct) { // Normal text
string s; s+=pos[0]; puts(s);
} else { // Format character
inPct = false;
switch (*pos) {
case '%': puts("%"); break;
case 'k': putbs(""); break;
case 'l': {
if (!lhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
else lhsp->iterateAndNext(*this);
break;
}
case 'r': {
if (!rhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
else rhsp->iterateAndNext(*this);
break;
}
case 't': {
if (!thsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); }
else thsp->iterateAndNext(*this);
break;
}
default:
nodep->v3fatalSrc("Unknown emitVerilog format code: %"<<pos[0]);
break;
}
}
}
}
virtual void visit(AstNodeTermop* nodep, AstNUser*) {
emitVerilogFormat(nodep, nodep->emitVerilog());
}
virtual void visit(AstNodeUniop* nodep, AstNUser*) {
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp());
}
virtual void visit(AstNodeBiop* nodep, AstNUser*) {
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp());
}
virtual void visit(AstNodeTriop* nodep, AstNUser*) {
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp(), nodep->thsp());
}
virtual void visit(AstAttrOf* nodep, AstNUser*) {
puts("$_ATTROF(");
nodep->fromp()->iterateAndNext(*this);
puts(")");
}
virtual void visit(AstNodeCond* nodep, AstNUser*) {
putbs("(");
nodep->condp()->iterateAndNext(*this); putbs(" ? ");
nodep->expr1p()->iterateAndNext(*this); putbs(" : ");
nodep->expr2p()->iterateAndNext(*this); puts(")");
}
virtual void visit(AstRange* nodep, AstNUser*) {
puts("[");
nodep->msbp()->iterateAndNext(*this); puts(":");
nodep->lsbp()->iterateAndNext(*this); puts("]");
}
virtual void visit(AstSel* nodep, AstNUser*) {
nodep->fromp()->iterateAndNext(*this); puts("[");
if (nodep->lsbp()->castConst()) {
if (nodep->widthp()->isOne()) {
nodep->lsbp()->iterateAndNext(*this);
} else {
puts(cvtToStr(nodep->lsbp()->castConst()->asInt()
+nodep->widthp()->castConst()->asInt()
-1));
puts(":");
nodep->lsbp()->iterateAndNext(*this);
}
} else {
nodep->lsbp()->iterateAndNext(*this); puts("+:");
nodep->widthp()->iterateAndNext(*this); puts("]");
}
puts("]");
}
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
if (nodep->dotted()!="") { puts(nodep->dotted()); puts("."); }
puts(nodep->name());
puts("(");
nodep->pinsp()->iterateAndNext(*this);
puts(")");
}
// Terminals
virtual void visit(AstVarRef* nodep, AstNUser*) {
puts(nodep->hiername());
puts(nodep->varp()->name());
}
virtual void visit(AstVarXRef* nodep, AstNUser*) {
puts(nodep->dotted());
puts(".");
puts(nodep->varp()->name());
}
virtual void visit(AstConst* nodep, AstNUser*) {
puts(nodep->num().ascii(true,true));
}
// Just iterate
virtual void visit(AstTopScope* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
virtual void visit(AstScope* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
virtual void visit(AstVar* nodep, AstNUser*) {
puts(nodep->verilogKwd());
puts(" ");
if (nodep->isSigned()) puts("signed ");
if (nodep->rangep()) {
puts("["+cvtToStr(nodep->msb())
+":"+cvtToStr(nodep->lsb())
+"] ");
}
puts(nodep->name());
for (AstRange* arrayp=nodep->arraysp(); arrayp; arrayp = arrayp->nextp()->castRange()) {
puts(" ["+cvtToStr(arrayp->msbConst())
+":"+cvtToStr(arrayp->lsbConst())
+"]");
}
puts(";\n");
}
virtual void visit(AstNodeText*, AstNUser*) {}
virtual void visit(AstTraceDecl*, AstNUser*) {}
virtual void visit(AstTraceInc*, AstNUser*) {}
// NOPs
virtual void visit(AstPragma*, AstNUser*) {}
virtual void visit(AstCell*, AstNUser*) {} // Handled outside the Visit class
// Default
virtual void visit(AstNode* nodep, AstNUser*) {
puts((string)"\n???? // "+nodep->typeName()+"\n");
nodep->iterateChildren(*this);
nodep->v3fatalSrc("Unknown node type reached emitter: "<<nodep->typeName());
}
public:
EmitVImp() {
m_suppressSemi = false;
m_modp = NULL;
}
void main(AstNode* nodep, V3OutCFile* ofp);
virtual ~EmitVImp() {}
};
//######################################################################
void EmitVImp::main(AstNode* modp, V3OutCFile* ofp) {
// Output a module, or overall netlist
m_ofp = ofp;
modp->accept(*this);
}
//######################################################################
// EmitV class functions
void V3EmitV::emitv() {
UINFO(2,__FUNCTION__<<": "<<endl);
if (1) {
// All-in-one file
V3OutVFile of (v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__Vout.v");
of.putsHeader();
EmitVImp imp;
imp.main(v3Global.rootp(), &of);
} else {
// Process each module in turn
for (AstModule* modp = v3Global.rootp()->modulesp(); modp; modp=modp->nextp()->castModule()) {
EmitVImp imp;
V3OutVFile of (v3Global.opt.makeDir()+"/"+imp.modClassName(modp)+"__Vout.v");
of.putsHeader();
imp.main(modp, &of);
}
}
}

35
src/V3EmitV.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Emit Verilog code for module tree
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3EMITV_H_
#define _V3EMITV_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3EmitV {
public:
static void emitv();
};
#endif // Guard

276
src/V3Error.cpp Normal file
View File

@ -0,0 +1,276 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Error handling
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "V3Error.h"
#ifndef _V3ERROR_NO_GLOBAL_
# include "V3Ast.h"
# include "V3Global.h"
# include "V3Stats.h"
#endif
//======================================================================
// Statics
FileLine FileLine::s_defaultFileLine = FileLine(EmptySecret());
int V3Error::s_errcnt = 0;
int V3Error::s_debugDefault = 0;
ostringstream V3Error::s_errorStr; // Error string being formed
V3ErrorCode V3Error::s_errorCode = V3ErrorCode::FATAL;
bool V3Error::s_describedEachWarn[V3ErrorCode::MAX];
bool V3Error::s_describedWarnings = false;
bool V3Error::s_pretendError[V3ErrorCode::MAX];
struct v3errorIniter {
v3errorIniter() { V3Error::init(); };
};
v3errorIniter v3errorInit;
//######################################################################
// ErrorCode class functions
V3ErrorCode::V3ErrorCode(const char* msgp) {
// Return error encoding for given string, or ERROR, which is a bad code
for (int codei=V3ErrorCode::FIRST_WARN; codei<V3ErrorCode::MAX; codei++) {
V3ErrorCode code = (V3ErrorCode)codei;
if (0==strcasecmp(msgp,code.ascii())) {
m_e = code; return;
}
}
m_e = V3ErrorCode::ERROR;
}
//######################################################################
// FileLine class functions
void FileLine::lineDirective(const char* textp) {
// Handle `line directive
// Skip `line
while (*textp && isspace(*textp)) textp++;
while (*textp && !isspace(*textp)) textp++;
while (*textp && (isspace(*textp) || *textp=='"')) textp++;
// Grab linenumber
const char *ln = textp;
while (*textp && !isspace(*textp)) textp++;
if (isdigit(*ln)) {
this->lineno(atoi(ln));
}
while (*textp && (isspace(*textp) || *textp=='"')) textp++;
// Grab filename
const char *fn = textp;
while (*textp && !(isspace(*textp) || *textp=='"')) textp++;
if (textp != fn) {
string strfn = fn;
strfn = strfn.substr(0, textp-fn);
this->filename(strfn);
}
//printf ("PPLINE %d '%s'\n", s_lineno, s_filename.c_str());
}
bool FileLine::warnOff(const string& msg, bool flag) {
V3ErrorCode code (msg.c_str());
if (code == V3ErrorCode::ERROR) {
return false;
} else {
warnOff(code, flag);
return true;
}
}
FileLine* FileLine::copyOrSameFileLine() {
// Return this, or a copy of this
// There are often more then one token per line, thus we use the
// same pointer as long as we're on the same line.
static FileLine* lastNewp = NULL;
if (lastNewp && *lastNewp == *this) {
return lastNewp;
}
FileLine* newp = new FileLine(this);
lastNewp = newp;
return newp;
}
const string FileLine::filebasename() const {
string name = filename();
string::size_type pos;
if ((pos = name.rfind("/")) != string::npos) {
name.erase(0,pos+1);
}
return name;
}
const string FileLine::profileFuncname() const {
// Return string that is OK as a function name - for profiling
string name = filebasename();
string::size_type pos;
if ((pos = name.find(".")) != string::npos) {
name = name.substr(0,pos);
}
while ((pos = name.find_first_not_of("abcdefghijlkmnopqrstuvwxyzABCDEFGHIJLKMNOPQRSTUVWXYZ0123456789_"))
!= string::npos) {
name.replace(pos, 1, "_");
}
name += "__"+cvtToStr(lineno());
return name;
}
string FileLine::ascii() const {
return filename()+":"+cvtToStr(lineno());
}
ostream& operator<<(ostream& os, FileLine* fileline) {
os <<fileline->ascii()<<": "<<hex;
return(os);
}
bool FileLine::warnIsOff(V3ErrorCode code) {
if (m_warnOff.test(code)) return true;
// UNOPTFLAT implies UNOPT
if (code==V3ErrorCode::UNOPT && m_warnOff.test(V3ErrorCode::UNOPTFLAT)) return true;
return false;
}
void FileLine::v3errorEnd(ostringstream& str) {
if (this && m_lineno) {
ostringstream nsstr;
nsstr<<this<<str.str();
if (warnIsOff(V3Error::errorCode())) V3Error::suppressThisWarning();
V3Error::v3errorEnd(nsstr);
} else {
V3Error::v3errorEnd(str);
}
}
//######################################################################
// V3Error class functions
void V3Error::init() {
for (int i=0; i<V3ErrorCode::MAX; i++) {
s_describedEachWarn[i] = false;
s_pretendError[i] = false;
}
pretendError(V3ErrorCode::BLKANDNBLK, true);
if (string(V3ErrorCode(V3ErrorCode::MAX).ascii()) != " MAX") {
v3fatalSrc("Enum table in V3ErrorCode::ascii() is munged");
}
}
string V3Error::lineStr (const char* filename, int lineno) {
ostringstream out;
const char* fnslashp = strrchr (filename, '/');
if (fnslashp) filename = fnslashp+1;
out<<filename<<":"<<dec<<lineno<<":";
const char* spaces = " ";
int numsp = out.str().length(); if (numsp>20) numsp = 20;
out<<(spaces + numsp);
return out.str();
}
void V3Error::incErrors() {
s_errcnt++;
if (errorCount() == MAX_ERRORS) { // Not >= as would otherwise recurse
v3fatal ("Exiting due to too many errors encountered\n");
}
}
void V3Error::abortIfErrors() {
if (errorCount()) {
v3fatal ("Exiting due to "<<dec<<errorCount()<<" warning(s)\n");
}
}
string V3Error::msgPrefix(V3ErrorCode code) {
if (code==V3ErrorCode::SUPPRESS) return "-arning-suppressed: ";
else if (code==V3ErrorCode::FATAL) return "%Error: ";
else if (code==V3ErrorCode::ERROR
|| s_pretendError[code]) return "%Error: ";
else return "%Warning-"+(string)code.ascii()+": ";
}
//======================================================================
// Global Functions
string V3Error::v3sform (const char* format, ...) {
static char msg[1000] = "";
va_list ap;
va_start(ap,format);
vsprintf(msg,format,ap);
va_end(ap);
string out = msg;
return out;
}
void V3Error::suppressThisWarning() {
if (s_errorCode>=V3ErrorCode::FIRST_WARN) {
V3Stats::addStatSum(string("Warnings, Suppressed ")+s_errorCode.ascii(), 1);
s_errorCode=V3ErrorCode::SUPPRESS;
}
}
void V3Error::v3abort () {
v3fatalSrc("v3abort called\n");
}
void V3Error::v3errorEnd (ostringstream& sstr) {
if (s_errorCode!=V3ErrorCode::SUPPRESS || debug()) {
cerr<<msgPrefix()<<sstr.str();
if (sstr.str()[sstr.str().length()-1] != '\n') {
cerr<<endl;
}
if (s_errorCode!=V3ErrorCode::SUPPRESS) {
if (!s_describedEachWarn[s_errorCode]
&& !s_pretendError[s_errorCode]) {
s_describedEachWarn[s_errorCode] = true;
if (s_errorCode>=V3ErrorCode::FIRST_WARN && !s_describedWarnings) {
cerr<<msgPrefix()<<"Use \"/* verilator lint_off "<<s_errorCode.ascii()
<<" */\" and lint_on around source to disable this message."<<endl;
s_describedWarnings = true;
}
if (s_errorCode.dangerous()) {
cerr<<msgPrefix()<<"*** See the manual before disabling this,"<<endl;
cerr<<msgPrefix()<<"else you may end up with different sim results."<<endl;
}
}
incErrors();
if (s_errorCode==V3ErrorCode::FATAL) {
static bool inFatal = false;
if (!inFatal) {
inFatal = true;
#ifndef _V3ERROR_NO_GLOBAL_
if (debug()) {
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree",99));
V3Stats::statsFinalAll(v3Global.rootp());
V3Stats::statsReport();
}
#endif
}
abort();
// exit(10);
}
}
}
}

198
src/V3Error.h Normal file
View File

@ -0,0 +1,198 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Error handling
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3ERROR_H_
#define _V3ERROR_H_ 1
#include "config.h"
#include <string>
#include <iostream>
#include <sstream>
#include <bitset>
//######################################################################
class V3ErrorCode {
public:
enum en {
SUPPRESS, // Warning suppressed by user
FATAL, // Kill the program
ERROR, // Error out, can't suppress
// Warning codes:
FIRST_WARN, // Just a code so the program knows where to start warnings
BLKANDNBLK, // Blocked and non-blocking assignments to same variable
CASEINCOMPLETE, // Case statement has missing values
CASEOVERLAP, // Case statements overlap
CASEX, // Casex
CMPCONST, // Comparison is constant due to limited range
COMBDLY, // Combinatorial delayed assignment
GENCLK, // Generated Clock
IMPLICIT, // Implicit wire
MULTIDRIVEN, // Driven from multiple blocks
UNDRIVEN, // No drivers
UNOPT, // Unoptimizable block
UNOPTFLAT, // Unoptimizable block after flattening
UNSIGNED, // Comparison is constant due to unsigned arithmetic
UNUSED, // No receivers
VARHIDDEN, // Hiding variable
WIDTH, // Width mismatch
MAX
// ***Add new elements below also***
};
enum en m_e;
inline V3ErrorCode () {};
inline V3ErrorCode (en _e) : m_e(_e) {};
V3ErrorCode (const char* msgp); // Matching code or ERROR
explicit inline V3ErrorCode (int _e) : m_e(static_cast<en>(_e)) {};
operator en () const { return m_e; };
const char* ascii() const {
const char* names[] = {
// Leading spaces indicate it can't be disabled.
" SUPPRESS", " FATAL", " ERROR",
" FIRST_WARN",
"BLKANDNBLK",
"CASEINCOMPLETE", "CASEOVERLAP", "CASEX", "CMPCONST",
"COMBDLY", "GENCLK", "IMPLICIT",
"MULTIDRIVEN",
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNSIGNED", "UNUSED",
"VARHIDDEN", "WIDTH",
" MAX"
};
return names[m_e];
};
// Warnings that warn about nasty side effects
bool dangerous() const { return ( m_e==COMBDLY );};
};
inline bool operator== (V3ErrorCode lhs, V3ErrorCode rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (V3ErrorCode lhs, V3ErrorCode::en rhs) { return (lhs.m_e == rhs); }
inline bool operator== (V3ErrorCode::en lhs, V3ErrorCode rhs) { return (lhs == rhs.m_e); }
inline ostream& operator<<(ostream& os, V3ErrorCode rhs) { return os<<rhs.ascii(); }
//######################################################################
class V3Error {
// Base class for any object that wants debugging and error reporting
private:
static bool s_describedWarnings; // Told user how to disable warns
static bool s_describedEachWarn[V3ErrorCode::MAX]; // Told user specifics about this warning
static bool s_pretendError[V3ErrorCode::MAX]; // Pretend this warning is an error
static int s_debugDefault; // Default debugging level
static int s_errcnt; // Error count
static ostringstream s_errorStr; // Error string being formed
static V3ErrorCode s_errorCode; // Error string being formed will abort
enum MaxErrors { MAX_ERRORS = 50 }; // Fatal after this may errors
static void incErrors();
V3Error() { cerr<<("Static class"); abort(); }
public:
// CREATORS
// ACCESSORS
static void debugDefault(int level) { s_debugDefault = level; }
static int debugDefault() { return s_debugDefault; }
static string msgPrefix(V3ErrorCode code=s_errorCode); // returns %Error/%Warn
static int errorCount() { return s_errcnt; }
// METHODS
static void init();
static void abortIfErrors();
static void suppressThisWarning(); // Suppress next %Warn if user has it off
static void pretendError(V3ErrorCode code, bool flag) { s_pretendError[code]=flag; }
static string v3sform (const char* format, ...);
static string lineStr (const char* filename, int lineno);
static V3ErrorCode errorCode() { return s_errorCode; }
// Internals for v3error()/v3fatal() macros only
// Error end takes the string stream to output, be careful to seek() as needed
static ostringstream& v3errorPrep (V3ErrorCode code) {
s_errorStr.str(""); s_errorCode=code; return s_errorStr; }
static ostringstream& v3errorStr () { return s_errorStr; }
static void v3abort();
static void v3errorEnd(ostringstream& sstr); // static, but often overridden in classes.
};
// Global versions, so that if the class doesn't define a operator, we get the functions anyways.
inline int debug() { return V3Error::debugDefault(); }
inline void v3errorEnd(ostringstream& sstr) { V3Error::v3errorEnd(sstr); }
// These allow errors using << operators: v3error("foo"<<"bar");
// Careful, you can't put () around msg, as you would in most macro definitions
#define v3fatal(msg) v3errorEnd(((V3Error::v3errorPrep(V3ErrorCode::FATAL)<<msg),V3Error::v3errorStr()));
#define v3error(msg) v3errorEnd(((V3Error::v3errorPrep(V3ErrorCode::ERROR)<<msg),V3Error::v3errorStr()));
#define v3warn(code,msg) v3errorEnd(((V3Error::v3errorPrep(V3ErrorCode::code)<<msg),V3Error::v3errorStr()));
// Use this instead of fatal() to mention the source code line.
#define v3fatalSrc(msg) v3fatal("Internal Error: "<<__FILE__<<":"<<dec<<__LINE__<<": "<<msg)
#define UINFO(level,stmsg) {if(debug()>=(level)) { cout<<"- "<<V3Error::lineStr(__FILE__,__LINE__)<<stmsg; }}
#define UINFONL(level,stmsg) {if(debug()>=(level)) { cout<<stmsg; } }
#define UDEBUGONLY(stmts) {stmts}
#define UASSERT(condition,stmsg) { if (!(condition)) { v3fatalSrc(stmsg); }}
// For use in V3Ast static functions only
#define UASSERT_STATIC(condition,stmsg) { if (!(condition)) { cerr<<"Internal Error: "<<__FILE__<<":"<<dec<<__LINE__<<(stmsg)<<endl; abort(); } }
#define V3ERROR_NA v3error("Internal: Unexpected Call")
//----------------------------------------------------------------------
template< class T> std::string cvtToStr (const T& t) {
ostringstream os; os<<t; return os.str();
}
//######################################################################
class FileLine {
// File and line number of an object, mostly for error reporting
int m_lineno;
string m_filename;
bitset<V3ErrorCode::MAX> m_warnOff;
static FileLine s_defaultFileLine;
struct EmptySecret {};
protected:
// User routines should never need to change line numbers
// We are storing pointers, so we CAN'T change them after initial reading.
friend class V3Read;
friend class V3PreLex;
void lineno(int num) { m_lineno = num; }
void filename(const string& name) { m_filename = name; }
void lineDirective(const char* textp);
void incLineno() { m_lineno++; }
FileLine* copyOrSameFileLine();
public:
FileLine (const string& filename, int lineno) { m_lineno=lineno; m_filename = filename; m_warnOff=s_defaultFileLine.m_warnOff;}
FileLine (FileLine* fromp) { m_lineno=fromp->lineno(); m_filename = fromp->filename(); m_warnOff=fromp->m_warnOff;};
FileLine (EmptySecret) { m_lineno=0; m_filename="COMMAND_LINE"; m_warnOff=0; } // Only for static constructor
static FileLine& defaultFileLine() { return s_defaultFileLine; }
int lineno () const { return m_lineno; }
string ascii() const;
const string filename () const { return m_filename; }
const string filebasename () const;
const char* cfilename () const { return m_filename.c_str(); }
const string profileFuncname() const;
void warnOff(V3ErrorCode code, bool flag) { m_warnOff.set(code,flag); } // Turn on/off warning messages on this line.
bool warnOff(const string& code, bool flag); // Returns 1 if ok
bool warnIsOff(V3ErrorCode code);
void warnResetDefault() { m_warnOff=s_defaultFileLine.m_warnOff; }
void v3errorEnd(ostringstream& str);
inline bool operator==(FileLine rhs) { return (m_lineno==rhs.m_lineno && m_filename==rhs.m_filename); }
};
ostream& operator<<(ostream& os, FileLine* fileline);
#endif // Guard

921
src/V3Expand.cpp Normal file
View File

@ -0,0 +1,921 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Add temporaries, such as for expand nodes
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2004-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Expand's Transformations:
//
// Each module:
// Expand verilated.h macros into internal micro optimizations (RTL)
// this will enable later optimizations.
// Wide operands become assignments to each word of the vector, (WORDSELs)
// Note in this case that the widthMin is not correct for the MSW of
// the vector. This must be accounted for if doing later constant
// propagation across signals.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include "V3Global.h"
#include "V3Expand.h"
#include "V3Ast.h"
//######################################################################
// Expand state, as a visitor of each AstNode
class ExpandVisitor : public AstNVisitor {
private:
// NODE STATE
// STATE
AstNode* m_stmtp; // Current statement
//int debug() { return 9; }
// METHODS
int longOrQuadWidth (AstNode* nodep) {
// Return 32 or 64...
return (nodep->width()+(VL_WORDSIZE-1)) & ~(VL_WORDSIZE-1);
}
V3Number notWideMask (AstNode* nodep) {
return V3Number (nodep->fileline(), VL_WORDSIZE, ~VL_MASK_I(nodep->widthMin()));
}
V3Number wordMask (AstNode* nodep) {
if (nodep->isWide()) {
return V3Number (nodep->fileline(), VL_WORDSIZE, VL_MASK_I(nodep->widthMin()));
} else {
V3Number zero (nodep->fileline(), nodep->widthMin(),0);
V3Number masksmall (nodep->fileline(), nodep->widthMin());
masksmall.opNot(zero);
V3Number mask (nodep->fileline(), longOrQuadWidth(nodep));
mask.opAssign(masksmall);
return mask;
}
}
void insertBefore (AstNode* placep, AstNode* newp) {
AstNRelinker linker;
placep->unlinkFrBack(&linker);
newp->addNext(placep);
linker.relink(newp);
}
void replaceWithDelete (AstNode* nodep, AstNode* newp) {
nodep->replaceWith(newp);
nodep->deleteTree(); nodep=NULL;
}
AstNode* newWordAssign (AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) {
AstAssign* newp = new AstAssign (placep->fileline(),
new AstWordSel (placep->fileline(),
lhsp->cloneTree(true),
new AstConst (placep->fileline(),
word)),
rhsp);
return newp;
}
void addWordAssign (AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) {
insertBefore (placep, newWordAssign(placep, word, lhsp, rhsp));
}
void addWordAssign (AstNodeAssign* placep, int word, AstNode* rhsp) {
addWordAssign (placep, word, placep->lhsp(), rhsp);
}
void fixCloneLvalue (AstNode* nodep) {
// In AstSel transforms, we call clone() on VarRefs that were lvalues,
// but are now being used on the RHS of the assignment
if (nodep->castVarRef()) nodep->castVarRef()->lvalue(false);
// Iterate
if (nodep->op1p()) fixCloneLvalue(nodep->op1p());
if (nodep->op2p()) fixCloneLvalue(nodep->op2p());
if (nodep->op3p()) fixCloneLvalue(nodep->op3p());
if (nodep->op4p()) fixCloneLvalue(nodep->op4p());
}
AstNode* newAstWordSelClone (AstNode* nodep, int word) {
// Get the specified word number from a wide array
// Or, if it's a long/quad, do appropriate conversion to wide
// Concat may pass negative word numbers, that means it wants a zero
if (nodep->isWide() && word>=0 && word<nodep->widthWords()) {
return new AstWordSel (nodep->fileline(),
nodep->cloneTree(true),
new AstConst(nodep->fileline(), word));
} else if (nodep->isQuad() && word==0) {
AstNode* quadfromp = nodep->cloneTree(true);
quadfromp->width(VL_QUADSIZE,quadfromp->widthMin());
return new AstCast (nodep->fileline(),
quadfromp,
VL_WORDSIZE);
} else if (nodep->isQuad() && word==1) {
AstNode* quadfromp = nodep->cloneTree(true);
quadfromp->width(VL_QUADSIZE,quadfromp->widthMin());
return new AstCast (nodep->fileline(),
new AstShiftR (nodep->fileline(),
quadfromp,
new AstConst (nodep->fileline(), VL_WORDSIZE),
VL_WORDSIZE),
VL_WORDSIZE);
} else if (!nodep->isWide() && !nodep->isQuad() && word==0) {
return nodep->cloneTree(true);
} else { // Out of bounds
return new AstConst (nodep->fileline(), 0);
}
}
AstNode* newWordGrabShift (FileLine* fl, int word, AstNode* lhsp, int shift) {
// Extract the expression to grab the value for the specified word, if it's the shift
// of shift bits from lhsp
AstNode* newp;
// Negative word numbers requested for lhs when it's "before" what we want.
// We get a 0 then.
int othword = word - shift/VL_WORDSIZE;
AstNode* llowp = newAstWordSelClone (lhsp, othword);
if (int loffset = VL_BITBIT_I(shift)) {
AstNode* lhip = newAstWordSelClone (lhsp, othword-1);
int nbitsonright = VL_WORDSIZE-loffset; // bits that end up in lword
newp = new AstOr
(fl,
new AstAnd(fl,
new AstConst (fl, VL_MASK_I(loffset)),
new AstShiftR (fl,
lhip,
new AstConst(fl, nbitsonright),
VL_WORDSIZE)),
new AstAnd(fl,
new AstConst (fl, ~VL_MASK_I(loffset)),
new AstShiftL(fl,
llowp,
new AstConst(fl, loffset),
VL_WORDSIZE)));
} else {
newp = llowp;
}
return newp;
}
AstNode* newSelBitWord(AstNode* lsbp, int wordAdder) {
// Return equation to get the VL_BITWORD of a constant or non-constant
if (lsbp->castConst()) {
return new AstConst (lsbp->fileline(),
wordAdder + VL_BITWORD_I(lsbp->castConst()->asInt()));
} else {
AstNode* shiftp = new AstShiftR (lsbp->fileline(),
lsbp->cloneTree(true),
new AstConst(lsbp->fileline(), VL_WORDSIZE_LOG2),
VL_WORDSIZE);
if (wordAdder != 0) {
shiftp = new AstAdd (lsbp->fileline(),
// This is indexing a arraysel, so a 32 bit constant is fine
new AstConst (lsbp->fileline(), wordAdder),
shiftp);
}
return shiftp;
}
}
AstNode* dropCondBound(AstNode* nodep) {
// Experimental only...
// If there's a CONDBOUND safety to keep arrays in bounds,
// we're going to AND it to a value that always fits inside a
// word, so we don't need it.
//if (nodep->castCondBound() && nodep->castCondBound()->lhsp()->castLte()) {
// nodep = nodep->castCondBound()->rhsp();
//}
return nodep;
}
AstNode* newSelBitBit(AstNode* lsbp) {
// Return equation to get the VL_BITBIT of a constant or non-constant
if (lsbp->castConst()) {
return new AstConst (lsbp->fileline(),
VL_BITBIT_I(lsbp->castConst()->asInt()));
} else {
return new AstAnd (lsbp->fileline(),
new AstConst(lsbp->fileline(), VL_WORDSIZE-1),
dropCondBound(lsbp)->cloneTree(true));
}
}
//====================
bool expandWide (AstNodeAssign* nodep, AstConst* rhsp) {
UINFO(8," Wordize ASSIGN(CONST) "<<nodep<<endl);
// -> {for each_word{ ASSIGN(WORDSEL(wide,#),WORDSEL(CONST,#))}}
if (rhsp->num().isFourState()) {
rhsp->v3error("Unsupported: 4-state numbers in this context");
}
for (int w=0; w<nodep->widthWords(); w++) {
V3Number num (nodep->fileline(), VL_WORDSIZE, rhsp->num().dataWord(w));
addWordAssign(nodep, w, new AstConst (nodep->fileline(), num));
}
return true;
}
//-------- Uniops
bool expandWide (AstNodeAssign* nodep, AstVarRef* rhsp) {
UINFO(8," Wordize ASSIGN(VARREF) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, newAstWordSelClone (rhsp, w));
}
return true;
}
bool expandWide (AstNodeAssign* nodep, AstArraySel* rhsp) {
UINFO(8," Wordize ASSIGN(ARRAYSEL) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, newAstWordSelClone (rhsp, w));
}
return true;
}
bool expandWide (AstNodeAssign* nodep, AstNot* rhsp) {
UINFO(8," Wordize ASSIGN(NOT) "<<nodep<<endl);
// -> {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }}
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, new AstNot (rhsp->fileline(),
newAstWordSelClone (rhsp->lhsp(), w)));
}
return true;
}
//-------- Biops
bool expandWide (AstNodeAssign* nodep, AstAnd* rhsp) {
UINFO(8," Wordize ASSIGN(AND) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, new AstAnd (nodep->fileline(),
newAstWordSelClone (rhsp->lhsp(), w),
newAstWordSelClone (rhsp->rhsp(), w)));
}
return true;
}
bool expandWide (AstNodeAssign* nodep, AstOr* rhsp) {
UINFO(8," Wordize ASSIGN(OR) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, new AstOr (nodep->fileline(),
newAstWordSelClone (rhsp->lhsp(), w),
newAstWordSelClone (rhsp->rhsp(), w)));
}
return true;
}
bool expandWide (AstNodeAssign* nodep, AstXor* rhsp) {
UINFO(8," Wordize ASSIGN(XOR) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, new AstXor (nodep->fileline(),
newAstWordSelClone (rhsp->lhsp(), w),
newAstWordSelClone (rhsp->rhsp(), w)));
}
return true;
}
bool expandWide (AstNodeAssign* nodep, AstXnor* rhsp) {
UINFO(8," Wordize ASSIGN(XNOR) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, new AstXnor (nodep->fileline(),
newAstWordSelClone (rhsp->lhsp(), w),
newAstWordSelClone (rhsp->rhsp(), w)));
}
return true;
}
//-------- Triops
bool expandWide (AstNodeAssign* nodep, AstNodeCond* rhsp) {
UINFO(8," Wordize ASSIGN(COND) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, new AstCond (nodep->fileline(),
rhsp->condp()->cloneTree(true),
newAstWordSelClone (rhsp->expr1p(), w),
newAstWordSelClone (rhsp->expr2p(), w)));
}
return true;
}
// VISITORS
virtual void visit(AstExtend* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->isWide()) {
// See under ASSIGN(EXTEND)
} else {
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newp = lhsp;
if (nodep->isQuad()) {
if (lhsp->isQuad()) {
lhsp->widthSignedFrom(nodep); // Just mark it, else nop
} else if (lhsp->isWide()) {
nodep->v3fatalSrc("extending larger thing into smaller?");
} else {
UINFO(8," EXTEND(q<-l) "<<nodep<<endl);
newp = new AstCast (nodep->fileline(), lhsp, nodep);
}
} else { // Long
if (lhsp->isQuad() || lhsp->isWide()) {
nodep->v3fatalSrc("extending larger thing into smaller?");
} else {
lhsp->widthSignedFrom(nodep); // Just mark it, else nop
}
}
replaceWithDelete(nodep,newp); nodep=NULL;
}
}
bool expandWide (AstNodeAssign* nodep, AstExtend* rhsp) {
UINFO(8," Wordize ASSIGN(EXTEND) "<<nodep<<endl);
int w=0;
for (w=0; w<rhsp->lhsp()->widthWords(); w++) {
addWordAssign(nodep, w, newAstWordSelClone (rhsp->lhsp(), w));
}
for (; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, new AstConst (rhsp->fileline(), 0));
}
return true;
}
virtual void visit(AstSel* nodep, AstNUser*) {
nodep->iterateChildren(*this);
// Remember, Sel's may have non-integer rhs, so need to optimize for that!
if (nodep->widthMin()!=(int)nodep->widthConst()) nodep->v3fatalSrc("Width mismatch");
if (nodep->backp()->castNodeAssign() && nodep==nodep->backp()->castNodeAssign()->lhsp()) {
// Sel is an LHS assignment select
} else if (nodep->isWide()) {
// See under ASSIGN(WIDE)
}
else if (nodep->fromp()->isWide()) {
UINFO(8," SEL(wide) "<<nodep<<endl);
// Selection amounts
// Check for constant shifts & save some constification work later.
// Grab lowest bit(s)
AstNode* lowwordp = new AstWordSel (nodep->fromp()->fileline(),
nodep->fromp()->cloneTree(true),
newSelBitWord(nodep->lsbp(), 0));
if (nodep->isQuad() && !lowwordp->isQuad()) lowwordp = new AstCast(nodep->fileline(), lowwordp, nodep);
AstNode* lowp = new AstShiftR (nodep->fileline(),
lowwordp,
newSelBitBit(nodep->lsbp()),
nodep->width());
// If > 1 bit, we might be crossing the word boundary
AstNode* midp=NULL;
V3Number zero (nodep->fileline(), longOrQuadWidth(nodep));
if (nodep->widthConst() > 1) {
AstNode* midwordp = // SEL(from,[1+wordnum])
new AstWordSel (nodep->fromp()->fileline(),
nodep->fromp()->cloneTree(true),
newSelBitWord(nodep->lsbp(), 1));
if (nodep->isQuad() && !midwordp->isQuad()) midwordp = new AstCast(nodep->fileline(), midwordp, nodep);
// If we're selecting bit zero, then all 32 bits in word 1 get shifted << by 32 bits
// else we need to form the lower word, so we << by 31 or less
// nbitsfromlow <= (lsb==0) ? 64-bitbit(lsb) : 32-bitbit(lsb)
AstNode* midshiftp = new AstSub (nodep->lsbp()->fileline(),
new AstConst(nodep->lsbp()->fileline(), VL_WORDSIZE),
newSelBitBit(nodep->lsbp()));
if (nodep->isQuad()) {
midshiftp =
new AstCond (nodep->fileline(),
new AstEq (nodep->fileline(),
new AstConst(nodep->fileline(), 0),
newSelBitBit(nodep->lsbp())),
new AstConst(nodep->lsbp()->fileline(), VL_WORDSIZE),
midshiftp);
}
AstNode* midmayp = new AstShiftL (nodep->fileline(),
midwordp,
midshiftp,
nodep->width());
if (nodep->isQuad()) {
midp = midmayp; // Always grab from two words
} else {
midp = new AstCond (nodep->fileline(),
new AstEq (nodep->fileline(),
new AstConst(nodep->fileline(), 0),
newSelBitBit(nodep->lsbp())),
new AstConst(nodep->fileline(), zero),
midmayp);
}
}
// If > 32 bits, we might be crossing the second word boundary
AstNode* hip=NULL;
if (nodep->widthConst() > VL_WORDSIZE) {
AstNode* hiwordp = // SEL(from,[2+wordnum])
new AstWordSel (nodep->fromp()->fileline(),
nodep->fromp()->cloneTree(true),
newSelBitWord(nodep->lsbp(), 2));
if (nodep->isQuad() && !hiwordp->isQuad()) hiwordp = new AstCast(nodep->fileline(), hiwordp, nodep);
AstNode* himayp =
new AstShiftL (nodep->fileline(),
hiwordp,
// nbitsfromlow_and_mid <= 64-bitbit(lsb)
new AstSub (nodep->lsbp()->fileline(),
new AstConst(nodep->lsbp()->fileline(), 64),
newSelBitBit(nodep->lsbp())),
nodep->width());
// if (frombit==0) then ignore, else use it
hip = new AstCond (nodep->fileline(),
new AstEq (nodep->fileline(),
new AstConst(nodep->fileline(), 0),
newSelBitBit(nodep->lsbp())),
new AstConst(nodep->fileline(), zero),
himayp);
}
AstNode* newp = lowp;
if (midp) newp = new AstOr (nodep->fileline(), midp, newp);
if (hip) newp = new AstOr (nodep->fileline(), hip, newp);
newp->widthFrom(nodep);
replaceWithDelete(nodep,newp); nodep=NULL;
}
else { // Long/Quad from Long/Quad
UINFO(8," SEL->SHIFT "<<nodep<<endl);
AstNode* fromp = nodep->fromp()->unlinkFrBack();
AstNode* lsbp = nodep->lsbp()->unlinkFrBack();
if (nodep->isQuad() && !fromp->isQuad()) fromp = new AstCast(nodep->fileline(), fromp, nodep);
AstNode* newp = new AstShiftR (nodep->fileline(),
fromp,
dropCondBound(lsbp),
nodep->width());
newp->widthSignedFrom(nodep);
if (!nodep->isQuad() && fromp->isQuad()) {
newp = new AstCast (newp->fileline(), newp, nodep);
}
newp->widthSignedFrom(nodep);
replaceWithDelete(nodep,newp); nodep=NULL;
}
}
bool expandWide (AstNodeAssign* nodep, AstSel* rhsp) {
if (nodep->widthMin()!=(int)rhsp->widthConst()) nodep->v3fatalSrc("Width mismatch");
if (rhsp->lsbp()->castConst() && VL_BITBIT_I(rhsp->lsbConst())==0) {
int lsb = rhsp->lsbConst();
UINFO(8," Wordize ASSIGN(SEL,align) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
addWordAssign(nodep, w, newAstWordSelClone (rhsp->fromp(), w + VL_BITWORD_I(lsb)));
}
return true;
} else {
UINFO(8," Wordize ASSIGN(EXTRACT,misalign) "<<nodep<<endl);
for (int w=0; w<nodep->widthWords(); w++) {
// Grab lowest bits
AstNode* lowwordp = new AstWordSel (rhsp->fileline(),
rhsp->fromp()->cloneTree(true),
newSelBitWord(rhsp->lsbp(), w));
AstNode* lowp = new AstShiftR (rhsp->fileline(),
lowwordp,
newSelBitBit(rhsp->lsbp()),
VL_WORDSIZE);
// Upper bits
V3Number zero (nodep->fileline(), VL_WORDSIZE, 0);
AstNode* midwordp = // SEL(from,[1+wordnum])
new AstWordSel (rhsp->fromp()->fileline(),
rhsp->fromp()->cloneTree(true),
newSelBitWord(rhsp->lsbp(), w+1));
AstNode* midshiftp = new AstSub (rhsp->lsbp()->fileline(),
new AstConst(rhsp->lsbp()->fileline(), VL_WORDSIZE),
newSelBitBit(rhsp->lsbp()));
AstNode* midmayp = new AstShiftL (rhsp->fileline(),
midwordp,
midshiftp,
VL_WORDSIZE);
AstNode* midp = new AstCond (rhsp->fileline(),
new AstEq (rhsp->fileline(),
new AstConst(rhsp->fileline(), 0),
newSelBitBit(rhsp->lsbp())),
new AstConst(rhsp->fileline(), zero),
midmayp);
AstNode* newp = new AstOr (nodep->fileline(), midp, lowp);
addWordAssign(nodep, w, newp);
}
return true;
}
}
bool expandLhs(AstNodeAssign* nodep, AstSel* lhsp) {
// Possibilities
// destp: wide or narrow
// rhsp: wide (destp must be wide), narrow, or 1 bit wide
// rhsp: may be allones and can remove AND NOT gate
// lsbp: constant or variable
// Yuk.
bool destwide = lhsp->fromp()->isWide();
bool ones = nodep->rhsp()->isAllOnesV();
if (lhsp->lsbp()->castConst()) {
// The code should work without this constant test, but it won't
// constify as nicely as we'd like.
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
AstNode* destp = lhsp->fromp()->unlinkFrBack();
int lsb = lhsp->lsbConst();
int msb = lhsp->msbConst();
V3Number maskset (nodep->fileline(), destp->widthMin());
for (int bit=lsb; bit<(msb+1); bit++) maskset.setBit(bit,1);
V3Number maskold (nodep->fileline(), destp->widthMin()); maskold.opNot(maskset);
if (destwide) {
UINFO(8," ASSIGNSEL(const,wide) "<<nodep<<endl);
for (int w=0; w<destp->widthWords(); w++) {
if (w>=VL_BITWORD_I(lsb) && w<=VL_BITWORD_I(msb)) {
// else we would just be setting it to the same exact value
AstNode* oldvalp = newAstWordSelClone (destp, w);
fixCloneLvalue(oldvalp);
if (!ones) oldvalp = new AstAnd (lhsp->fileline(),
new AstConst (lhsp->fileline(), maskold.dataWord(w)),
oldvalp);
addWordAssign(nodep, w,
destp,
new AstOr (lhsp->fileline(),
oldvalp,
newWordGrabShift(lhsp->fileline(), w,
rhsp, lsb)));
}
}
} else {
UINFO(8," ASSIGNSEL(const,narrow) "<<nodep<<endl);
if (destp->isQuad() && !rhsp->isQuad()) rhsp = new AstCast(nodep->fileline(), rhsp, nodep);
AstNode* oldvalp = destp->cloneTree(true);
fixCloneLvalue(oldvalp);
if (!ones) oldvalp = new AstAnd (lhsp->fileline(),
new AstConst (lhsp->fileline(), maskold),
oldvalp);
AstNode* newp
= new AstOr (lhsp->fileline(),
oldvalp,
new AstShiftL (lhsp->fileline(),
rhsp,
new AstConst (lhsp->fileline(), lsb),
destp->width()));
newp = new AstAssign (nodep->fileline(), destp, newp);
insertBefore(nodep,newp);
}
return true;
}
else { // non-const RHS
if (destwide && lhsp->widthConst()==1) {
UINFO(8," ASSIGNSEL(varlsb,wide,1bit) "<<nodep<<endl);
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
AstNode* destp = lhsp->fromp()->unlinkFrBack();
AstNode* oldvalp = new AstWordSel (lhsp->fileline(),
destp->cloneTree(true),
newSelBitWord(lhsp->lsbp(), 0));
fixCloneLvalue(oldvalp);
if (!ones)
oldvalp = new AstAnd (lhsp->fileline(),
new AstNot (lhsp->fileline(),
new AstShiftL (lhsp->fileline(),
new AstConst (nodep->fileline(),1),
// newSelBitBit may exceed the MSB of this variable.
// That's ok as we'd just AND with a larger value,
// but oldval would clip the upper bits to sanity
newSelBitBit(lhsp->lsbp()),
VL_WORDSIZE)),
oldvalp);
AstNode* newp = new AstOr (lhsp->fileline(),
oldvalp,
new AstShiftL (lhsp->fileline(),
rhsp,
lhsp->lsbp()->cloneTree(true),
VL_WORDSIZE));
newp = new AstAssign (nodep->fileline(),
new AstWordSel (nodep->fileline(),
destp,
newSelBitWord(lhsp->lsbp(), 0)),
newp);
insertBefore(nodep,newp);
return true;
}
else if (destwide) {
UINFO(8," ASSIGNSEL(varlsb,wide) -- NoOp -- "<<nodep<<endl);
// For wide destp, we can either form a equation for every destination word,
// with the appropriate long equation of if it's being written or not.
// Or, we can use a LHS variable arraysel with non-constant index to set the vector.
// Doing the variable arraysel is better for globals and large arrays,
// doing every word is better for temporaries and if we're setting most words
// since it may result in better substitution optimizations later.
// This results in so much code, we're better off leaving a function call.
// Reconsider if we get subexpression elimination.
return false;
} else {
UINFO(8," ASSIGNSEL(varlsb,narrow) "<<nodep<<endl);
//nodep->dumpTree(cout,"- old: ");
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
AstNode* destp = lhsp->fromp()->unlinkFrBack();
AstNode* oldvalp = destp->cloneTree(true);
fixCloneLvalue(oldvalp);
V3Number maskwidth (nodep->fileline(), destp->widthMin());
for (int bit=0; bit<(int)lhsp->widthConst(); bit++) maskwidth.setBit(bit,1);
if (destp->isQuad() && !rhsp->isQuad()) rhsp = new AstCast(nodep->fileline(), rhsp, nodep);
if (!ones)
oldvalp = new AstAnd (lhsp->fileline(),
new AstNot (lhsp->fileline(),
new AstShiftL (lhsp->fileline(),
new AstConst (nodep->fileline(),
maskwidth),
lhsp->lsbp()->cloneTree(true),
destp->width())),
oldvalp);
AstNode* newp
= new AstOr (lhsp->fileline(),
oldvalp,
new AstShiftL (lhsp->fileline(),
rhsp,
lhsp->lsbp()->cloneTree(true),
destp->width()));
newp = new AstAssign (nodep->fileline(), destp, newp);
//newp->dumpTree(cout,"- new: ");
insertBefore(nodep,newp);
return true;
}
}
}
virtual void visit(AstConcat* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->isWide()) {
// See under ASSIGN(WIDE)
} else {
UINFO(8," CONCAT "<<nodep<<endl);
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
int rhsshift = rhsp->widthMin();
if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCast(nodep->fileline(), lhsp, nodep);
if (nodep->isQuad() && !rhsp->isQuad()) rhsp = new AstCast(nodep->fileline(), rhsp, nodep);
AstNode* newp = new AstOr (nodep->fileline(),
new AstShiftL (nodep->fileline(),
lhsp,
new AstConst (nodep->fileline(), rhsshift),
nodep->width()),
rhsp);
newp->widthFrom(nodep); // Unsigned
replaceWithDelete(nodep,newp); nodep=NULL;
}
}
bool expandWide (AstNodeAssign* nodep, AstConcat* rhsp) {
UINFO(8," Wordize ASSIGN(CONCAT) "<<nodep<<endl);
// Lhs or Rhs may be word, long, or quad.
// newAstWordSelClone nicely abstracts the difference.
int rhsshift = rhsp->rhsp()->widthMin();
for (int w=0; w<rhsp->widthWords(); w++) {
addWordAssign(nodep, w,
new AstOr (rhsp->fileline(),
newWordGrabShift(rhsp->fileline(), w,
rhsp->lhsp(), rhsshift),
newAstWordSelClone (rhsp->rhsp(), w)));
}
return true;
}
virtual void visit(AstReplicate* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->isWide()) {
// See under ASSIGN(WIDE)
} else {
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newp;
int lhswidth = lhsp->widthMin();
if (lhswidth==1) {
UINFO(8," REPLICATE(w1) "<<nodep<<endl);
newp = new AstUnaryMin (nodep->fileline(),
lhsp);
} else {
UINFO(8," REPLICATE "<<nodep<<endl);
AstConst* constp = nodep->rhsp()->castConst();
if (!constp) nodep->v3fatalSrc("Replication value isn't a constant. Checked earlier!");
uint32_t times = constp->asInt();
if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCast(nodep->fileline(), lhsp, nodep);
newp = lhsp->cloneTree(true);
for (unsigned repnum=1; repnum<times; repnum++) {
int rhsshift = repnum*lhswidth;
newp = new AstOr (nodep->fileline(),
new AstShiftL (nodep->fileline(),
lhsp->cloneTree(true),
new AstConst (nodep->fileline(), rhsshift),
nodep->width()),
newp);
newp->widthFrom(nodep); // Unsigned
}
lhsp->deleteTree(); // Never used
}
newp->widthFrom(nodep); // Unsigned
replaceWithDelete(nodep,newp); nodep=NULL;
}
}
bool expandWide (AstNodeAssign* nodep, AstReplicate* rhsp) {
UINFO(8," Wordize ASSIGN(REPLICATE) "<<nodep<<endl);
AstNode* lhsp = rhsp->lhsp();
int lhswidth = lhsp->widthMin();
AstConst* constp = rhsp->rhsp()->castConst();
if (!constp) rhsp->v3fatalSrc("Replication value isn't a constant. Checked earlier!");
uint32_t times = constp->asInt();
for (int w=0; w<rhsp->widthWords(); w++) {
AstNode* newp;
if (lhswidth==1) {
newp = new AstUnaryMin (nodep->fileline(), lhsp->cloneTree(true));
newp->width(VL_WORDSIZE,VL_WORDSIZE);
} else {
newp = newAstWordSelClone (lhsp, w);
for (unsigned repnum=1; repnum<times; repnum++) {
newp = new AstOr (nodep->fileline(),
newWordGrabShift(rhsp->fileline(), w, lhsp,
lhswidth*repnum),
newp);
}
}
addWordAssign(nodep, w, newp);
}
return true;
}
virtual void visit(AstChangeXor* nodep, AstNUser*) {
nodep->iterateChildren(*this);
UINFO(8," Wordize ChangeXor "<<nodep<<endl);
// -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}}
AstNode* newp = NULL;
for (int w=0; w<nodep->lhsp()->widthWords(); w++) {
AstNode* eqp = new AstXor (nodep->fileline(),
newAstWordSelClone (nodep->lhsp(), w),
newAstWordSelClone (nodep->rhsp(), w));
newp = (newp==NULL) ? eqp : (new AstOr (nodep->fileline(), newp, eqp));
}
replaceWithDelete(nodep,newp); nodep=NULL;
}
void visitEqNeq(AstNodeBiop* nodep) {
nodep->iterateChildren(*this);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize EQ/NEQ "<<nodep<<endl);
// -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}}
AstNode* newp = NULL;
for (int w=0; w<nodep->lhsp()->widthWords(); w++) {
AstNode* eqp = new AstXor (nodep->fileline(),
newAstWordSelClone (nodep->lhsp(), w),
newAstWordSelClone (nodep->rhsp(), w));
newp = (newp==NULL) ? eqp : (new AstOr (nodep->fileline(), newp, eqp));
}
if (nodep->castNeq()) {
newp = new AstNeq (nodep->fileline(),
new AstConst (nodep->fileline(), 0), newp);
} else {
newp = new AstEq (nodep->fileline(),
new AstConst (nodep->fileline(), 0), newp);
}
replaceWithDelete(nodep,newp); nodep=NULL;
}
}
virtual void visit(AstEq* nodep, AstNUser*) { visitEqNeq (nodep); }
virtual void visit(AstNeq* nodep, AstNUser*) { visitEqNeq (nodep); }
virtual void visit(AstRedOr* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDOR "<<nodep<<endl);
// -> (0!={or{for each_word{WORDSEL(lhs,#)}}}
AstNode* newp = NULL;
for (int w=0; w<nodep->lhsp()->widthWords(); w++) {
AstNode* eqp = newAstWordSelClone (nodep->lhsp(), w);
newp = (newp==NULL) ? eqp : (new AstOr (nodep->fileline(), newp, eqp));
}
newp = new AstNeq (nodep->fileline(),
new AstConst (nodep->fileline(), 0), newp);
replaceWithDelete(nodep,newp); nodep=NULL;
} else {
UINFO(8," REDOR->EQ "<<nodep<<endl);
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
V3Number zero (nodep->fileline(), longOrQuadWidth(nodep));
AstNode* newp = new AstNeq (nodep->fileline(),
new AstConst (nodep->fileline(), zero),
lhsp);
replaceWithDelete(nodep,newp); nodep=NULL;
}
}
virtual void visit(AstRedAnd* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDAND "<<nodep<<endl);
// -> (0!={and{for each_word{WORDSEL(lhs,#)}}}
AstNode* newp = NULL;
for (int w=0; w<nodep->lhsp()->widthWords(); w++) {
AstNode* eqp = newAstWordSelClone (nodep->lhsp(), w);
if (w==nodep->lhsp()->widthWords()-1) {
// Rather then doing a (slowish) ==##, we OR in the bits that aren't part of the mask
eqp = new AstOr (nodep->fileline(),
new AstConst (nodep->fileline(), notWideMask(nodep->lhsp())),
eqp);
}
newp = (newp==NULL) ? eqp : (new AstAnd (nodep->fileline(), newp, eqp));
}
newp = new AstEq (nodep->fileline(),
new AstConst (nodep->fileline(), ~0), newp);
replaceWithDelete(nodep, newp); nodep=NULL;
} else {
UINFO(8," REDAND->EQ "<<nodep<<endl);
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newp = new AstEq (nodep->fileline(),
new AstConst (nodep->fileline(), wordMask(lhsp)),
lhsp);
replaceWithDelete(nodep,newp); nodep=NULL;
}
}
virtual void visit(AstRedXor* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->lhsp()->isWide()) {
UINFO(8," Wordize REDXOR "<<nodep<<endl);
// -> (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}}
AstNode* newp = NULL;
for (int w=0; w<nodep->lhsp()->widthWords(); w++) {
AstNode* eqp = newAstWordSelClone (nodep->lhsp(), w);
newp = (newp==NULL) ? eqp : (new AstXor (nodep->fileline(), newp, eqp));
}
newp = new AstRedXor (nodep->fileline(), newp);
replaceWithDelete(nodep, newp); nodep=NULL;
}
// We don't reduce non-wide XORs, as its more efficient to use a temp register,
// which the inlined function does nicely.
}
virtual void visit(AstNodeStmt* nodep, AstNUser*) {
m_stmtp = nodep;
nodep->iterateChildren(*this);
m_stmtp = NULL;
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
m_stmtp = nodep;
nodep->iterateChildren(*this);
bool did = false;
if (nodep->isWide() && ((nodep->lhsp()->castVarRef()
&& !nodep->lhsp()->castVarRef()->varp()->isSc())
|| nodep->lhsp()->castArraySel())) {
if (AstConst* rhsp = nodep->rhsp()->castConst()) {
did = expandWide(nodep,rhsp);
} else if (AstVarRef* rhsp = nodep->rhsp()->castVarRef()) {
if (rhsp->varp()->isSc()) {
// Still need special access function
} else {
did = expandWide(nodep,rhsp);
}
} else if (AstSel* rhsp = nodep->rhsp()->castSel()) {
did = expandWide(nodep,rhsp);
} else if (AstArraySel* rhsp = nodep->rhsp()->castArraySel()) {
did = expandWide(nodep,rhsp);
} else if (AstConcat* rhsp = nodep->rhsp()->castConcat()) {
did = expandWide(nodep,rhsp);
} else if (AstReplicate* rhsp = nodep->rhsp()->castReplicate()) {
did = expandWide(nodep,rhsp);
} else if (AstAnd* rhsp = nodep->rhsp()->castAnd()) {
did = expandWide(nodep,rhsp);
} else if (AstOr* rhsp = nodep->rhsp()->castOr()) {
did = expandWide(nodep,rhsp);
} else if (AstNot* rhsp = nodep->rhsp()->castNot()) {
did = expandWide(nodep,rhsp);
} else if (AstXor* rhsp = nodep->rhsp()->castXor()) {
did = expandWide(nodep,rhsp);
} else if (AstXnor* rhsp = nodep->rhsp()->castXnor()) {
did = expandWide(nodep,rhsp);
} else if (AstNodeCond* rhsp = nodep->rhsp()->castNodeCond()) {
did = expandWide(nodep,rhsp);
}
} else if (AstSel* lhsp = nodep->lhsp()->castSel()) {
did = expandLhs(nodep,lhsp);
}
// Cleanup common code
if (did) {
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
m_stmtp = NULL;
}
//--------------------
// Default: Just iterate
virtual void visit(AstVar*, AstNUser*) {} // Don't hit varrefs under vars
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
ExpandVisitor() {
m_stmtp=NULL;
}
virtual ~ExpandVisitor() {}
void main(AstNode* nodep) {
AstNode::userClearTree(); // userp() used on entire tree
nodep->accept(*this);
}
};
//----------------------------------------------------------------------
// Top loop
//######################################################################
// Expand class functions
void V3Expand::expandAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
ExpandVisitor visitor;
visitor.main(nodep);
}

35
src/V3Expand.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Expansion of wide operator macros to C operators
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3EXPAND_H_
#define _V3EXPAND_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Expand {
public:
static void expandAll(AstNetlist* nodep);
};
#endif // Guard

469
src/V3File.cpp Normal file
View File

@ -0,0 +1,469 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: File stream wrapper that understands indentation
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config.h"
#include <stdarg.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iomanip>
#include <memory>
#include "V3Global.h"
#include "V3File.h"
#include "V3PreShell.h"
//######################################################################
// V3File Internal state
class V3FileDependImp {
// TYPES
class DependFile {
// A single file
bool m_target; // True if write, else read
string m_filename; // Filename
struct stat m_stat; // Stat information
public:
DependFile(const string& filename, bool target)
: m_target(target), m_filename(filename) {
m_stat.st_mtime = 0;
}
~DependFile() {}
const string& filename() const { return m_filename; }
bool target() const { return m_target; }
off_t size() const { return m_stat.st_size; }
time_t mtime() const { return m_stat.st_mtime; }
void loadStats() {
if (!m_stat.st_mtime) {
int err = stat(filename().c_str(), &m_stat);
if (err!=0) {
m_stat.st_mtime = 1;
// Not a error... This can occur due to `line directives in the .vpp files
UINFO(1,"-Info: File not statable: "<<filename()<<endl);
}
}
}
bool operator<(const DependFile& rhs) const { return filename()<rhs.filename(); };
};
// MEMBERS
set<string> m_filenameSet; // Files generated (elim duplicates)
set<DependFile> m_filenameList; // Files sourced/generated
static string stripQuotes(const string& in) {
string pretty = in;
string::size_type pos;
while ((pos=pretty.find("\"")) != string::npos) pretty.replace(pos, 1, "_");
while ((pos=pretty.find("\n")) != string::npos) pretty.replace(pos, 1, "_");
return pretty;
}
public:
// ACCESSOR METHODS
void addSrcDepend(const string& filename) {
if (m_filenameSet.find(filename) == m_filenameSet.end()) {
m_filenameSet.insert(filename);
DependFile df (filename, false);
df.loadStats(); // Get size now, in case changes during the run
m_filenameList.insert(df);
}
}
void addTgtDepend(const string& filename) {
if (m_filenameSet.find(filename) == m_filenameSet.end()) {
m_filenameSet.insert(filename);
m_filenameList.insert(DependFile (filename, true));
}
}
void writeDepend(const string& filename);
void writeTimes(const string& filename, const string& cmdline);
bool checkTimes(const string& filename, const string& cmdline);
};
V3FileDependImp dependImp; // Depend implementation class
//######################################################################
// V3FileDependImp
inline void V3FileDependImp::writeDepend(const string& filename) {
const auto_ptr<ofstream> ofp (V3File::new_ofstream(filename));
if (ofp->fail()) v3fatalSrc("Can't write "<<filename);
for (set<DependFile>::iterator iter=m_filenameList.begin();
iter!=m_filenameList.end(); ++iter) {
if (iter->target()) {
*ofp<<iter->filename()<<" ";
}
}
*ofp<<" : ";
*ofp<<v3Global.opt.bin();
*ofp<<" ";
*ofp<<V3PreShell::dependFiles();
*ofp<<" ";
for (set<DependFile>::iterator iter=m_filenameList.begin();
iter!=m_filenameList.end(); ++iter) {
if (!iter->target()) {
*ofp<<iter->filename()<<" ";
}
}
*ofp<<endl;
}
inline void V3FileDependImp::writeTimes(const string& filename, const string& cmdlineIn) {
const auto_ptr<ofstream> ofp (V3File::new_ofstream(filename));
if (ofp->fail()) v3fatalSrc("Can't write "<<filename);
string cmdline = stripQuotes(cmdlineIn);
*ofp<<"C \""<<cmdline<<"\""<<endl;
sync(); // Push files so sizes look correct
for (set<DependFile>::iterator iter=m_filenameList.begin();
iter!=m_filenameList.end(); ++iter) {
// Read stats of files we create after we're done making them (execpt for this file, of course)
DependFile* dfp = (DependFile*)&(*iter);
dfp->loadStats();
off_t showSize = iter->size();
if (dfp->filename() == filename) showSize=0; // We're writing it, so need to ignore it
*ofp<<(iter->target()?"T":"S")<<" ";
*ofp<<" "<<setw(8)<<showSize;
*ofp<<" "<<setw(11)<<iter->mtime();
*ofp<<" \""<<iter->filename()<<"\"";
*ofp<<endl;
}
}
inline bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn) {
const auto_ptr<ifstream> ifp (V3File::new_ifstream_nodepend(filename));
if (ifp->fail()) {
UINFO(2," --check-times failed: no input "<<filename<<endl);
return false;
}
{
char chkDir; *ifp>>chkDir;
char quote; *ifp>>quote;
string chkCmdline; getline(*ifp, chkCmdline, '"');
string cmdline = stripQuotes(cmdlineIn);
if (cmdline != chkCmdline) {
UINFO(2," --check-times failed: different command line\n");
return false;
}
}
while (!ifp->eof()) {
char chkDir; *ifp>>chkDir;
off_t chkSize; *ifp>>chkSize;
if (ifp->eof()) break; // Needed to read final whitespace before found eof
time_t chkMtime; *ifp>>chkMtime;
char quote; *ifp>>quote;
string chkFilename; getline(*ifp, chkFilename, '"');
//UINFO(9," got d="<<chkDir<<" s="<<chkSize<<" mt="<<chkMtime<<" fn = "<<chkFilename<<endl);
struct stat chkStat;
int err = stat(chkFilename.c_str(), &chkStat);
if (err!=0) {
UINFO(2," --check-times failed: missing "<<chkFilename<<endl);
return false;
}
if (filename != chkFilename) { // See above; we were writing it at the time...
// We'd like this rule:
//if (!(chkStat.st_size == chkSize
// && chkStat.st_mtime == chkMtime) {
// However NFS messes us up, as there might be some data outstanding when
// we determined the original size. For safety, we know the creation time
// must be within a few second window... call it 20 sec.
if (!(chkStat.st_size >= chkSize
&& chkStat.st_mtime >= chkMtime
&& chkStat.st_mtime <= (chkMtime + 20))) {
UINFO(2," --check-times failed: out-of-date "<<chkFilename
<<"; "<<chkStat.st_size<<"=?"<<chkSize
<<" "<<chkStat.st_mtime<<"=?"<<chkMtime<<endl);
return false;
}
}
}
return true;
}
//######################################################################
// V3File
void V3File::addSrcDepend(const string& filename) {
dependImp.addSrcDepend(filename);
}
void V3File::addTgtDepend(const string& filename) {
dependImp.addTgtDepend(filename);
}
void V3File::writeDepend(const string& filename) {
dependImp.writeDepend(filename);
}
void V3File::writeTimes(const string& filename, const string& cmdline) {
dependImp.writeTimes(filename, cmdline);
}
bool V3File::checkTimes(const string& filename, const string& cmdline) {
return dependImp.checkTimes(filename, cmdline);
}
//######################################################################
// V3OutFile: A class for printing to a file, with automatic indentation of C++ code.
V3OutFile::V3OutFile(const string& filename)
: m_lineno(1), m_column(0), m_nobreak(false), m_prependIndent(true), m_indentLevel(0)
, m_declAlign(0), m_declPadNum(0) {
if ((m_fp = V3File::new_fopen_w(filename.c_str())) == NULL) {
v3fatal("Cannot write "<<filename);
}
}
V3OutFile::~V3OutFile() {
if (m_fp) fclose(m_fp);
m_fp = NULL;
}
//----------------------------------------------------------------------
const char* V3OutFile::indentStr(int num) {
// Indent the specified levelsber of spaces. Use tabs as possible.
static char str[MAXSPACE+20];
char* cp = str;
if (num>MAXSPACE) num=MAXSPACE;
while (num>=8) {
*cp++ = '\t';
num -= 8;
}
while (num>0) {
*cp++ = ' ';
num --;
}
*cp++ = '\0';
return (str);
}
const string V3OutFile::indentSpaces(int num) {
// Indent the specified number of spaces. Use spaces.
static char str[MAXSPACE+20];
char* cp = str;
if (num>MAXSPACE) num=MAXSPACE;
while (num>0) {
*cp++ = ' ';
num --;
}
*cp++ = '\0';
string st (str);
return (st);
}
bool V3OutFile::tokenStart(const char* cp, const char* cmp) {
while (*cmp == *cp) { cp++; cmp++; }
if (*cmp) return false;
if (*cp && !isspace(*cp)) return false;
return true;
}
#define VERILOG_INDENTS 0 // No verilog output yet, speed things up
bool V3OutFile::tokenEnd(const char* cp) {
return (tokenStart(cp,"end")
|| tokenStart(cp,"endcase")
|| tokenStart(cp,"endmodule"));
}
int V3OutFile::endLevels (const char *strg) {
int levels=m_indentLevel;
const char* cp=strg;
while (isspace(*cp)) cp++;
switch (*cp) {
case '\n': // Newlines.. No need for whitespace before it
return (0);
case '#': // Preproc directive
return (0);
}
{
// label/public/private: Deindent by 2 spaces
const char* mp=cp;
for (; isalnum(*mp); mp++) ;
if (mp[0]==':' && mp[1]!=':') return (levels-INDBLK/2);
}
// We want "} else {" to be one level to the left of normal
for (const char* cp=strg; *cp; cp++) {
switch (*cp) {
case '}':
case ')':
levels-=INDBLK;
break;
case 'e':
if (VERILOG_INDENTS && tokenEnd(cp)) {
levels-=INDBLK;
}
break;
case '\t':
case ' ':
break; // Continue
default:
return (levels); // Letter
}
}
return (levels);
}
void V3OutFile::puts (const char *strg) {
if (m_prependIndent) {
putsNoTracking(indentStr(endLevels(strg)));
m_prependIndent = false;
}
bool wordstart = true;
for (const char* cp=strg; *cp; cp++) {
putcNoTracking (*cp);
switch (*cp) {
case '\n':
m_lineno++;
wordstart = true;
if (cp[1]=='\0') {
m_prependIndent = true; // Add the indent later, may be a indentInc/indentDec called between now and then
} else {
m_prependIndent = false;
putsNoTracking(indentStr(endLevels(cp+1)));
}
break;
case ' ':
wordstart = true;
break;
case '\t':
wordstart = true;
break;
case '{':
indentInc();
break;
case '(':
indentInc();
m_parenVec.push(m_column);
break;
case '}':
indentDec();
break;
case ')':
if (!m_parenVec.empty()) m_parenVec.pop();
indentDec();
break;
case 'b':
if (wordstart && VERILOG_INDENTS && tokenStart(cp,"begin")) {
indentInc();
}
wordstart = false;
break;
case 'c':
if (wordstart && VERILOG_INDENTS && (tokenStart(cp,"case")
|| tokenStart(cp,"casex")
|| tokenStart(cp,"casez"))) {
indentInc();
}
wordstart = false;
break;
case 'e':
if (wordstart && VERILOG_INDENTS && tokenEnd(cp)) {
indentDec();
}
wordstart = false;
break;
case 'm':
if (wordstart && VERILOG_INDENTS && tokenStart(cp,"module")) {
indentInc();
}
wordstart = false;
break;
default:
wordstart = false;
break;
}
}
}
void V3OutFile::putBreakExpr () {
if (!m_parenVec.empty()) putBreak();
}
// Add a line break if too wide
void V3OutFile::putBreak () {
if (!m_nobreak) {
//char s[1000]; sprintf(s,"{%d,%d}",m_column,m_parenVec.top()); putsNoTracking(s);
if (exceededWidth()) {
putcNoTracking('\n');
if (!m_parenVec.empty()) putsNoTracking(indentStr(m_parenVec.top()));
}
}
}
void V3OutFile::putsNoTracking (const char *strg) {
// Don't track {}'s, probably because it's a $display format string
for (const char* cp=strg; *cp; cp++) {
putcNoTracking (*cp);
}
}
void V3OutFile::putcNoTracking (char chr) {
switch (chr) {
case '\n':
m_lineno++;
m_column=0;
m_nobreak=true;
break;
case '\t':
m_column = ((m_column + 9)/8)*8;
break;
case ' ':
case '(':
case '|':
case '&':
m_column++;
break;
default:
m_column++;
m_nobreak=false;
break;
}
fputc (chr, m_fp);
}
void V3OutFile::putAlign (int align, int size, const char* prefix) {
if (size==0) size=align;
int alignSize = size; if (alignSize>8) alignSize=8;
int padsize = alignSize - (m_declAlign % alignSize);
if (padsize && padsize!=alignSize) {
puts("char\t");
puts(prefix);
puts("__VpadToAlign"+cvtToStr(m_declPadNum)
+"["+cvtToStr(padsize)+"];\n");
m_declAlign += padsize;
m_declPadNum++;
}
m_declAlign += size;
}
//----------------------------------------------------------------------
// Simple wrappers
void V3OutFile::printf (const char *fmt...) {
char sbuff[5000];
va_list ap;
va_start(ap,fmt);
vsprintf(sbuff,fmt,ap);
va_end(ap);
this->puts(sbuff);
}

116
src/V3File.h Normal file
View File

@ -0,0 +1,116 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: File stream wrapper that understands indentation
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3FILE_H_
#define _V3FILE_H_ 1
#include "config.h"
#include "V3Error.h"
#include <stdio.h>
#include <stack>
#include <set>
#include <fstream>
//============================================================================
// V3File: Create streams, recording dependency information
class V3File {
public:
static ifstream* new_ifstream(const string& filename) {
addSrcDepend(filename);
return new_ifstream_nodepend (filename);
}
static ifstream* new_ifstream_nodepend(const string& filename) {
return new ifstream(filename.c_str());
}
static ofstream* new_ofstream(const string& filename, bool append=false) {
addTgtDepend(filename);
if (append) {
return new ofstream(filename.c_str(), ios::app);
} else {
return new ofstream(filename.c_str());
}
}
static FILE* new_fopen_w(const string& filename) {
addTgtDepend(filename);
return fopen(filename.c_str(),"w");
}
// Dependencies
static void addSrcDepend(const string& filename);
static void addTgtDepend(const string& filename);
static void writeDepend(const string& filename);
static void writeTimes(const string& filename, const string& cmdline);
static bool checkTimes(const string& filename, const string& cmdline);
};
//============================================================================
// V3OutFile: A class for printing to a file, with automatic indentation of C++ code.
class V3OutFile {
FILE* m_fp;
int m_lineno;
int m_column;
int m_nobreak; // Basic operator or begin paren, don't break next
bool m_prependIndent;
int m_indentLevel; // Current {} indentation
int m_declAlign; // Byte alignment of next declaration
int m_declPadNum; // Pad variable number
stack<int> m_parenVec; // Stack of columns where last ( was
int endLevels(const char* strg);
static const char* indentStr(int levels);
enum MiscConsts {
INDBLK = 4, // Indentation per block level
WIDTH = 50, // Width after which to break at ,'s
MAXSPACE = 80}; // After this indent, stop indenting more
public:
V3OutFile(const string& filename);
~V3OutFile();
// ACCESSORS
int column() const { return m_column; }
// METHODS
void printf(const char* fmt...) VL_ATTR_PRINTF(2);
void puts(const char* strg);
void puts(const string& strg) { puts(strg.c_str()); }
void putsNoTracking(const char* strg);
void putsNoTracking(const string& strg) { putsNoTracking(strg.c_str()); }
void putBreak(); // Print linebreak if line is too wide
void putBreakExpr(); // Print linebreak in expression if line is too wide
void putAlign(int align, int size=0/*=align*/, const char* prefix=""); // Declare a variable, with natural alignment
void putbs(const char* strg) { putBreakExpr(); puts(strg); }
void putbs(const string& strg) { putBreakExpr(); puts(strg); }
bool exceededWidth() const { return m_column > WIDTH; }
bool tokenStart(const char* cp, const char* cmp);
bool tokenEnd(const char* cp);
void indentInc() { m_indentLevel += INDBLK; };
void indentDec() { m_indentLevel -= INDBLK;
UASSERT(m_indentLevel>=0,"Underflow of indentation\n");
}
void blockInc() { m_parenVec.push(m_indentLevel + INDBLK); }
void blockDec() { if (!m_parenVec.empty()) m_parenVec.pop(); }
// STATIC METHODS
static const string indentSpaces(int levels);
private:
void putcNoTracking(char chr);
};
#endif // Guard

704
src/V3Gate.cpp Normal file
View File

@ -0,0 +1,704 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Gate optimizations, such as wire elimination
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Gate's Transformations:
//
// Extract a graph of the *entire* netlist with cells expanded
// Perform constant optimization across the graph
// Create VARSCOPEs for any variables we can rip out
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include <iomanip>
#include <vector>
#include <list>
#include "V3Global.h"
#include "V3Gate.h"
#include "V3Ast.h"
#include "V3Graph.h"
#include "V3Const.h"
#include "V3Stats.h"
typedef list<AstNodeVarRef*> GateVarRefList;
//######################################################################
class GateBaseVisitor : public AstNVisitor {
public:
//int debug() { return 9; }
};
//######################################################################
// Support classes
class GateEitherVertex : public V3GraphVertex {
AstScope* m_scopep;
bool m_reducible; // True if this node should be able to be eliminated
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) {}
virtual ~GateEitherVertex() {}
// Accessors
virtual string dotStyle() const { return m_consumed?"":"dotted"; }
AstScope* scopep() const { return m_scopep; }
bool reducible() const { return m_reducible; }
void setConsumed(const char* consumedReason) {
m_consumed = true;
//UINFO(0,"\t\tSetConsumed "<<consumedReason<<" "<<this<<endl);
}
bool consumed() const { return m_consumed; }
void clearReducible(const char* nonReducibleReason) {
m_reducible = false;
//UINFO(0," NR: "<<nonReducibleReason<<" "<<name()<<endl);
}
};
class GateVarVertex : public GateEitherVertex {
AstVarScope* m_varScp;
bool m_isTop;
bool m_isClock;
public:
GateVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
: GateEitherVertex(graphp, scopep), m_varScp(varScp), m_isTop(false)
, m_isClock(false) {}
virtual ~GateVarVertex() {}
// Accessors
AstVarScope* varScp() const { return m_varScp; }
virtual string name() const { return (cvtToStr((void*)m_varScp)+" "+varScp()->name()); }
virtual string dotColor() const { return "blue"; }
void setIsTop() { m_isTop = true; }
void setIsClock() { m_isClock = true; setConsumed("isclk"); }
bool isTop() const { return m_isTop; }
bool isClock() const { return m_isClock; }
};
class GateLogicVertex : public GateEitherVertex {
AstNode* m_nodep;
AstActive* m_activep; // Under what active; NULL is ok (under cfunc or such)
bool m_slow; // In slow block
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
virtual string name() const { return (cvtToStr((void*)m_nodep)+"@"+scopep()->name()); }
virtual string dotColor() const { return "yellow"; }
AstNode* nodep() const { return m_nodep; }
AstActive* activep() const { return m_activep; }
bool slow() const { return m_slow; }
};
//######################################################################
// Is this a simple math expression with a single input and single output?
class GateOkVisitor : public GateBaseVisitor {
private:
// RETURN STATE
bool m_isSimple; // Set false when we know it isn't simple
GateVarRefList m_rhsVarRefs; // VarRefs on rhs of assignment
AstNode* m_substTreep; // What to replace the variable with
// 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)
// METHODS
void clearSimple(const char* because) {
if (m_isSimple) {
m_isSimple = false;
UINFO(9, "Clear simple "<<because<<endl);
}
}
// VISITORS
virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
nodep->iterateChildren(*this);
// We only allow a LHS ref for the var being set, and a RHS ref for something else being read.
if (nodep->varScopep()->varp()->isSc()) {
clearSimple("SystemC sig"); // Don't want to eliminate the VL_ASSIGN_SI's
}
if (nodep->lvalue()) {
if (m_lhsVarRef) clearSimple(">1 lhs varRefs");
m_lhsVarRef = nodep;
} else {
if (m_rhsVarRefs.size()>1) {
AstNodeVarRef* lastRefp = m_rhsVarRefs.back();
if (0) { // Diable the multiple-input optimization
clearSimple(">1 rhs varRefs");
} else {
if (m_buffersOnly) clearSimple(">1 rhs varRefs");
if (!nodep->varScopep()->varp()->gateMultiInputOptimizable()
// We didn't check multiInput on the first varref, so check it here
|| !lastRefp->varScopep()->varp()->gateMultiInputOptimizable()) {
clearSimple("!gateMultiInputOptimizable");
}
}
}
m_rhsVarRefs.push_back(nodep);
}
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
m_substTreep = nodep->rhsp();
if (!nodep->lhsp()->castNodeVarRef())
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
// V3Const cleans up any NOTs by flipping the edges for us
if (m_buffersOnly
&& !(nodep->rhsp()->castVarRef()
// Until NEW_ORDERING, avoid making non-clocked logic into clocked,
// as it slows down the verilator_sim_benchmark
|| (nodep->rhsp()->castNot()
&& nodep->rhsp()->castNot()->lhsp()->castVarRef()
&& nodep->rhsp()->castNot()->lhsp()->castVarRef()->varp()->isUsedClock())
)) {
clearSimple("Not a buffer (goes to a clock)");
}
}
//--------------------
// Default
virtual void visit(AstNode* nodep, AstNUser*) {
// *** Special iterator
if (!m_isSimple) return; // Fastpath
if (!nodep->isGateOptimizable()
|| !nodep->isSplittable()) {
UINFO(5, "Non optimizable type: "<<nodep<<endl);
clearSimple("Non optimizable type");
}
else nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
GateOkVisitor(AstNode* nodep, bool buffersOnly) {
m_isSimple = true;
m_substTreep = NULL;
m_buffersOnly = buffersOnly;
m_lhsVarRef = NULL;
nodep->accept(*this);
if (!m_substTreep) {
clearSimple("No assignment found\n");
}
if (debug()>=9 && !m_isSimple) {
nodep->dumpTree(cout,"\tgate!Ok: ");
}
}
virtual ~GateOkVisitor() {}
// PUBLIC METHODS
bool isSimple() const { return m_isSimple; }
AstNode* substTree() const { return m_substTreep; }
const GateVarRefList& rhsVarRefs() const {
return m_rhsVarRefs;
}
};
//######################################################################
// Gate class functions
class GateVisitor : public GateBaseVisitor {
private:
// NODE STATE
//Entire netlist:
// AstVarScope::userp -> GateVarVertex* for usage var, 0=not set yet
// {statement}Node::userp -> GateLogicVertex* for this statement
// STATE
V3Graph m_graph; // Scoreboard of var usages/dependencies
GateLogicVertex* m_logicVertexp; // Current statement being tracked, NULL=ignored
AstScope* m_scopep; // Current scope being processed
AstModule* m_modp; // Current module
AstActive* m_activep; // Current active
bool m_activeReducible; // Is activation block reducible?
bool m_inSenItem; // Underneath AstSenItem; any varrefs are clocks
bool m_inSlow; // Inside a slow structure
V3Double0 m_statSigs; // Statistic tracking
V3Double0 m_statRefs; // Statistic tracking
// METHODS
void iterateNewStmt(AstNode* nodep, const char* nonReducibleReason, const char* consumeReason) {
if (m_scopep) {
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);
}
if (consumeReason) m_logicVertexp->setConsumed(consumeReason);
if (nodep->castSenItem()) m_logicVertexp->setConsumed("senItem");
nodep->iterateChildren(*this);
m_logicVertexp = NULL;
}
}
GateVarVertex* makeVarVertex(AstVarScope* varscp) {
GateVarVertex* vertexp = (GateVarVertex*)(varscp->userp());
if (!vertexp) {
UINFO(6,"New vertex "<<varscp<<endl);
vertexp = new GateVarVertex(&m_graph, m_scopep, varscp);
varscp->userp(vertexp);
if (varscp->varp()->isSigPublic()) {
// Public signals shouldn't be changed, pli code might be messing with them
vertexp->clearReducible("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->setConsumed("isTop");
}
if (varscp->varp()->isUsedClock()) vertexp->setConsumed("clock");
}
if (m_inSenItem) vertexp->setIsClock();
return vertexp;
}
void optimizeSignals(bool allowMultiIn);
void optimizeElimVar(AstVarScope* varscp, AstNode* logicp, AstNode* consumerp);
void consumedMark();
void consumedMarkRecurse(GateEitherVertex* vertexp);
void consumedMove();
void replaceAssigns();
// VISITORS
virtual void visit(AstNetlist* nodep, AstNUser*) {
//VV***** We reset userp() and user2p
AstNode::userClearTree();
nodep->iterateChildren(*this);
//if (debug()>6) m_graph.dump();
if (debug()>6) m_graph.dumpDotFilePrefixed("gate_pre");
m_graph.removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue);
m_graph.dumpDotFilePrefixed("gate_simp");
// Find gate interconnect and optimize
m_graph.userClearVertices(); // vertex->user(): bool. True indicates we've set it as consumed
// Get rid of buffers first,
optimizeSignals(false);
// Then propagate more complicated equations
optimizeSignals(true);
consumedMark();
m_graph.dumpDotFilePrefixed("gate_opt");
// Rewrite assignments
consumedMove();
replaceAssigns();
}
virtual void visit(AstModule* nodep, AstNUser*) {
m_modp = nodep;
m_activeReducible = true;
nodep->iterateChildren(*this);
}
virtual void visit(AstScope* nodep, AstNUser*) {
UINFO(4," SCOPE "<<nodep<<endl);
m_scopep = nodep;
m_logicVertexp = NULL;
nodep->iterateChildren(*this);
m_scopep = NULL;
}
virtual void visit(AstActive* nodep, AstNUser*) {
// Create required blocks and add to module
UINFO(4," BLOCK "<<nodep<<endl);
m_activeReducible = !(nodep->hasClocked()); // Seq logic outputs aren't reducible
m_activep = nodep;
nodep->iterateChildren(*this);
m_activep = NULL;
m_activeReducible = true;
}
virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
if (m_scopep) {
if (!m_logicVertexp) nodep->v3fatalSrc("Var ref not under a logic block\n");
AstVarScope* varscp = nodep->varScopep();
if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp\n");
GateVarVertex* varvertexp = makeVarVertex(varscp);
UINFO(5," VARREF to "<<varscp<<endl);
// We use weight of one; if we ref the var more then once, when we simplify,
// the weight will increase
if (nodep->lvalue()) {
new V3GraphEdge(&m_graph, m_logicVertexp, varvertexp, 1);
} else {
new V3GraphEdge(&m_graph, varvertexp, m_logicVertexp, 1);
}
}
}
virtual void visit(AstAlways* nodep, AstNUser*) {
iterateNewStmt(nodep, (nodep->isJustOneBodyStmt()?NULL:"Multiple Stmts"), NULL);
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
iterateNewStmt(nodep, "User C Function", "User C Function");
}
virtual void visit(AstSenItem* nodep, AstNUser*) {
m_inSenItem = true;
iterateNewStmt(nodep, NULL, NULL);
m_inSenItem = false;
}
virtual void visit(AstInitial* nodep, AstNUser*) {
bool lastslow = m_inSlow;
m_inSlow = true;
iterateNewStmt(nodep, (nodep->isJustOneBodyStmt()?NULL:"Multiple Stmts"), NULL);
m_inSlow = lastslow;
}
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
iterateNewStmt(nodep, NULL, NULL);
}
virtual void visit(AstAssignW* nodep, AstNUser*) {
iterateNewStmt(nodep, NULL, NULL);
}
virtual void visit(AstTraceInc* nodep, AstNUser*) {
bool lastslow = m_inSlow;
m_inSlow = true;
iterateNewStmt(nodep, "Tracing", "Tracing");
m_inSlow = lastslow;
}
virtual void visit(AstConcat* nodep, AstNUser*) {
if (nodep->backp()->castNodeAssign() && nodep->backp()->castNodeAssign()->lhsp()==nodep) {
nodep->v3fatalSrc("Concat on LHS of assignment; V3Const should have deleted it\n");
}
nodep->iterateChildren(*this);
}
//--------------------
// Default
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->isOutputter() && m_logicVertexp) m_logicVertexp->setConsumed("outputter");
}
public:
// CONSTUCTORS
GateVisitor(AstNode* nodep) {
m_logicVertexp = NULL;
m_scopep = NULL;
m_modp = NULL;
m_activep = NULL;
m_activeReducible = true;
m_inSenItem = false;
m_inSlow = false;
nodep->accept(*this);
}
virtual ~GateVisitor() {
V3Stats::addStat("Optimizations, Gate sigs deleted", m_statSigs);
V3Stats::addStat("Optimizations, Gate inputs replaced", m_statRefs);
}
};
//----------------------------------------------------------------------
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
if (!vvertexp->isTop() // Ok if top inputs are driverless
&& !vvertexp->varScp()->varp()->initp()
&& !vvertexp->varScp()->varp()->isSigPublic()) {
UINFO(1, "No drivers "<<vvertexp->varScp()<<endl);
if (0) {
// If we warned here after constant propagation, what the user considered
// reasonable logic may have disappeared. Issuing a warning would
// thus be confusing.
vvertexp->varScp()->varp()->v3warn(UNDRIVEN,"Signal has no drivers "
<<vvertexp->scopep()->prettyName()<<"."
<<vvertexp->varScp()->varp()->prettyName());
}
}
}
else if (!vvertexp->inSize1()) {
vvertexp->clearReducible("size!1"); // Can't deal with more then one src
}
// Reduce it?
if (!vvertexp->reducible()) {
UINFO(8, "SigNotRed "<<vvertexp->name()<<endl);
} else {
UINFO(8, "Sig "<<vvertexp->name()<<endl);
GateLogicVertex* logicVertexp = dynamic_cast<GateLogicVertex*>
(vvertexp->inBeginp()->fromp());
UINFO(8, " From "<<logicVertexp->name()<<endl);
AstNode* logicp = logicVertexp->nodep();
if (logicVertexp->reducible()) {
// Can we eliminate?
GateOkVisitor okVisitor(logicp, vvertexp->isClock());
bool multiInputs = okVisitor.rhsVarRefs().size() > 1;
// Was it ok?
bool doit = okVisitor.isSimple();
if (doit && multiInputs) {
if (!allowMultiIn) doit = false;
// Doit if one input, or not used, or used only once, ignoring traces
int n=0;
for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->top());
if (!consumeVertexp->slow()) { // Not tracing or other slow path junk
if (edgep->top()->outBeginp()) { // Destination is itself used
n += edgep->weight();
}
}
if (n>1) {
doit = false;
break;
}
}
}
if (!doit) {
if (allowMultiIn && (debug()>=9)) {
UINFO(9, "Not ok simp"<<okVisitor.isSimple()<<" mi"<<multiInputs
<<" ob"<<vvertexp->outBeginp()<<" on"<<(vvertexp->outBeginp()?vvertexp->outBeginp()->outNextp():0)
<<" "<<vvertexp->name()
<<endl);
for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->top());
UINFO(9, " edge "<<edgep<<" to: "<<consumeVertexp->nodep()<<endl);
}
for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->fromp());
UINFO(9, " edge "<<edgep<<" from: "<<consumeVertexp->nodep()<<endl);
}
}
}
else {
AstNode* substp = okVisitor.substTree();
if (debug()>=5) logicp->dumpTree(cout,"\telimVar: ");
if (debug()>=5) substp->dumpTree(cout,"\t subst: ");
m_statSigs++;
while (V3GraphEdge* edgep = vvertexp->outBeginp()) {
GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->top());
AstNode* consumerp = consumeVertexp->nodep();
optimizeElimVar(vvertexp->varScp(), substp, consumerp);
// If the new replacement referred to a signal,
// Correct the graph to point to this new generating variable
for (GateVarRefList::const_iterator it = okVisitor.rhsVarRefs().begin();
it != okVisitor.rhsVarRefs().end(); ++it) {
AstVarScope* newvarscp = (*it)->varScopep();
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();
}
}
// Remove the edge
edgep->unlinkDelete(); edgep=NULL;
m_statRefs++;
}
// Remove input links
while (V3GraphEdge* edgep = vvertexp->inBeginp()) {
edgep->unlinkDelete(); edgep=NULL;
}
// Clone tree so we remember it for tracing, and keep the pointer
// to the "ALWAYS" part of the tree as part of this statement
// That way if a later signal optimization that retained a pointer to the always
// can optimize it further
logicp->unlinkFrBack();
vvertexp->varScp()->valuep(logicp);
logicp = NULL;
// Mark the vertex so we don't mark it as being unconsumed in the next step
vvertexp->user(true);
logicVertexp->user(true);
}
}
}
}
}
}
void GateVisitor::replaceAssigns() {
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {
// Take the Comments/assigns that were moved to the VarScope and change them to a
// simple value assignment
AstVarScope* vscp = vvertexp->varScp();
if (vscp->valuep() && !vscp->valuep()->castNodeMath()) {
//if (debug()>9) vscp->dumpTree(cout, "-vscPre: ");
while (AstNode* delp=vscp->valuep()->castComment()) {
delp->unlinkFrBack()->deleteTree(); delp=NULL;
}
if (AstInitial* delp=vscp->valuep()->castInitial()) {
AstNode* bodyp=delp->bodysp();
bodyp->unlinkFrBackWithNext();
delp->replaceWith(bodyp);
delp->deleteTree(); delp=NULL;
}
if (AstAlways* delp=vscp->valuep()->castAlways()) {
AstNode* bodyp=delp->bodysp();
bodyp->unlinkFrBackWithNext();
delp->replaceWith(bodyp);
delp->deleteTree(); delp=NULL;
}
if (AstNodeAssign* delp=vscp->valuep()->castNodeAssign()) {
AstNode* rhsp=delp->rhsp();
rhsp->unlinkFrBack();
delp->replaceWith(rhsp);
delp->deleteTree(); delp=NULL;
}
//if (debug()>9) {vscp->dumpTree(cout, "-vscDone: "); cout<<endl;}
if (!vscp->valuep()->castNodeMath()
|| vscp->valuep()->nextp()) {
vscp->dumpTree(cout, "vscStrange: ");
vscp->v3fatalSrc("Value of varscope not mathematical\n");
}
}
}
}
}
//----------------------------------------------------------------------
void GateVisitor::consumedMark() {
// Propagate consumed signals backwards to all producers into a consumed node
m_graph.userClearVertices();
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
GateEitherVertex* evertexp = (GateEitherVertex*)vertexp;
if (!evertexp->user() && evertexp->consumed()) {
consumedMarkRecurse(evertexp);
}
}
}
void GateVisitor::consumedMarkRecurse(GateEitherVertex* vertexp) {
if (vertexp->user()) return; // Already marked
vertexp->user(true);
if (!vertexp->consumed()) vertexp->setConsumed("propagated");
// Walk sources and mark them too
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
GateEitherVertex* eFromVertexp = (GateEitherVertex*)edgep->fromp();
consumedMarkRecurse(eFromVertexp);
}
}
void GateVisitor::consumedMove() {
// Remove unused logic (logic that doesn't hit a combo block or a display statement)
// We need the "usually" block logic to do a better job at this
for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(vertexp)) {
if (!vvertexp->consumed() && !vvertexp->user()) {
UINFO(8, "Unconsumed "<<vvertexp->varScp()<<endl);
}
}
if (GateLogicVertex* lvertexp = dynamic_cast<GateLogicVertex*>(vertexp)) {
AstNode* nodep = lvertexp->nodep();
AstActive* oldactp = lvertexp->activep(); // NULL under cfunc
if (!lvertexp->consumed() && oldactp) {
// Eventually: Move the statement to a new active block with "tracing-on" sensitivity
UINFO(8," Remove unconsumed "<<nodep<<endl);
nodep->unlinkFrBack();
pushDeletep(nodep); nodep=NULL;
}
}
}
}
//######################################################################
// Push constant into expressions and reevaluate
class GateElimVisitor : public GateBaseVisitor {
private:
// NODE STATE
// STATE
AstVarScope* m_elimVarScp; // Variable being eliminated
AstNode* m_replaceTreep; // What to replace the variable with
bool m_didReplace; // Did we do any replacements
// VISITORS
virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
if (nodep->varScopep() == m_elimVarScp) {
// Substitute in the new tree
// It's possible we substitute into something that will be reduced more later
// however, as we never delete the top Always/initial statement, all should be well.
m_didReplace = true;
if (nodep->lvalue()) nodep->v3fatalSrc("Can't replace lvalue assignments with const var");
AstNode* substp = m_replaceTreep->cloneTree(false);
if (nodep->castNodeVarRef()
&& substp->castNodeVarRef()
&& nodep->same(substp)) {
// Prevent a infinite loop...
substp->v3fatalSrc("Replacing node with itself; perhaps circular logic?");
}
nodep->replaceWith(substp);
nodep->deleteTree(); nodep=NULL;
}
}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
virtual ~GateElimVisitor() {}
GateElimVisitor(AstNode* nodep, AstVarScope* varscp, AstNode* replaceTreep) {
m_didReplace = false;
m_elimVarScp = varscp;
m_replaceTreep = replaceTreep;
nodep->accept(*this);
}
bool didReplace() const { return m_didReplace; }
};
void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp) {
if (debug()>=5) consumerp->dumpTree(cout,"\telimUsePre: ");
GateElimVisitor elimVisitor (consumerp, varscp, substp);
if (elimVisitor.didReplace()) {
if (debug()>=9) consumerp->dumpTree(cout,"\telimUseCns: ");
//Caution: Can't let V3Const change our handle to consumerp, such as by
// optimizing away this assignment, etc.
V3Const::constifyTree(consumerp);
if (debug()>=5) consumerp->dumpTree(cout,"\telimUseDne: ");
}
}
//######################################################################
// Convert VARSCOPE(ASSIGN(default, VARREF)) to just VARSCOPE(default)
class GateDeassignVisitor : public GateBaseVisitor {
private:
// VISITORS
virtual void visit(AstVarScope* nodep, AstNUser*) {
if (AstNodeAssign* assp = nodep->valuep()->castNodeAssign()) {
UINFO(5," Removeassign "<<assp<<endl);
AstNode* valuep = assp->rhsp();
valuep->unlinkFrBack();
assp->replaceWith(valuep);
assp->deleteTree(); assp=NULL;
}
}
// Speedups
virtual void visit(AstVar* nodep, AstNUser*) {}
virtual void visit(AstActive* nodep, AstNUser*) {}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
GateDeassignVisitor(AstNode* nodep) {
nodep->accept(*this);
}
virtual ~GateDeassignVisitor() {}
};
//######################################################################
// Gate class functions
void V3Gate::gateAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
GateVisitor visitor (nodep);
GateDeassignVisitor deassign (nodep);
}

35
src/V3Gate.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Break always into sensitivity block domains
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3GATE_H_
#define _V3GATE_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Gate {
public:
static void gateAll(AstNetlist* nodep);
};
#endif // Guard

224
src/V3GenClk.cpp Normal file
View File

@ -0,0 +1,224 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Generated Clock repairs
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// GENCLK TRANSFORMATIONS:
// Follow control-flow graph with assignments and var usages
// ASSIGNDLY to variable later used as clock requires change detect
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include "V3Global.h"
#include "V3GenClk.h"
#include "V3Ast.h"
//######################################################################
// GenClk state, as a visitor of each AstNode
class GenClkBaseVisitor : public AstNVisitor {
protected:
//static int debug() { return 9; }
};
//######################################################################
// GenClk Read
class GenClkRenameVisitor : public GenClkBaseVisitor {
private:
// NODE STATE
// Cleared on top scope
// AstVarScope::user2() -> AstVarScope*. Signal replacing activation with
// AstVarRef::user3() -> bool. Signal is replaced activation (already done)
// STATE
AstActive* m_activep; // Inside activate statement
AstModule* m_topModp; // Top module
AstScope* m_scopetopp; // Scope under TOPSCOPE
// METHODS
AstVarScope* genInpClk(AstVarScope* vscp) {
if (vscp->user2p()) {
return vscp->user2p()->castNode()->castVarScope();
} else {
AstVar* varp = vscp->varp();
string newvarname = "__VinpClk__"+vscp->scopep()->nameDotless()+"__"+varp->name();
// Create: VARREF(inpclk)
// ...
// ASSIGN(VARREF(inpclk), VARREF(var))
AstVar* newvarp = new AstVar (varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
m_topModp->addStmtp(newvarp);
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopetopp, newvarp);
m_scopetopp->addVarp(newvscp);
AstAssign* asninitp = new AstAssign (vscp->fileline(),
new AstVarRef(vscp->fileline(), newvscp, true),
new AstVarRef(vscp->fileline(), vscp, false));
m_scopetopp->addFinalClkp(asninitp);
//
vscp->user2p(newvscp);
return newvscp;
}
}
// VISITORS
virtual void visit(AstTopScope* nodep, AstNUser*) {
AstNode::user2ClearTree(); // userp() used on entire tree
AstScope* scopep = nodep->scopep()->castScope();
if (!scopep) nodep->v3fatalSrc("No scope found on top level");
m_scopetopp = scopep;
nodep->iterateChildren(*this);
}
//----
virtual void visit(AstVarRef* nodep, AstNUser*) {
// Consumption/generation of a variable,
AstVarScope* vscp = nodep->varScopep();
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
if (m_activep && !nodep->user3()) {
nodep->user3(true);
if (vscp->isCircular()) {
UINFO(8," VarActReplace "<<nodep<<endl);
// Replace with the new variable
AstVarScope* newvscp = genInpClk(vscp);
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), newvscp, nodep->lvalue());
nodep->replaceWith(newrefp);
pushDeletep(nodep); nodep=NULL;
}
}
}
virtual void visit(AstActive* nodep, AstNUser*) {
m_activep = nodep;
nodep->sensesp()->iterateChildren(*this);
m_activep = NULL;
nodep->iterateChildren(*this);
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
//-----
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTRUCTORS
GenClkRenameVisitor(AstTopScope* nodep, AstModule* topModp) {
m_topModp = topModp;
m_scopetopp = NULL;
m_activep = NULL;
nodep->accept(*this);
}
virtual ~GenClkRenameVisitor() {}
};
//######################################################################
// GenClk Read
class GenClkReadVisitor : public GenClkBaseVisitor {
private:
// NODE STATE
// Cleared on top scope
// AstVarScope::user() -> bool. Set when the var has been used as clock
// STATE
AstActive* m_activep; // Inside activate statement
AstNodeAssign* m_assignp; // Inside assigndly statement
AstModule* m_topModp; // Top module
// VISITORS
virtual void visit(AstTopScope* nodep, AstNUser*) {
AstNode::userClearTree(); // userp() used on entire tree
nodep->iterateChildren(*this);
{
// Make the new clock signals and replace any activate references
// See rename, it does some AstNode::userClearTree()'s
GenClkRenameVisitor visitor (nodep, m_topModp);
}
}
virtual void visit(AstModule* nodep, AstNUser*) {
// Only track the top scopes, not lower level functions
if (nodep->isTop()) {
m_topModp = nodep;
nodep->iterateChildren(*this);
}
}
virtual void visit(AstCCall* nodep, AstNUser*) {
nodep->iterateChildren(*this);
// Enter the function and trace it
nodep->funcp()->accept(*this);
}
//----
virtual void visit(AstVarRef* nodep, AstNUser*) {
// Consumption/generation of a variable,
AstVarScope* vscp = nodep->varScopep();
if (!vscp) nodep->v3fatalSrc("Scope not assigned");
if (m_activep) {
UINFO(8," VarAct "<<nodep<<endl);
vscp->user(true);
}
if (m_assignp && nodep->lvalue() && vscp->user()) {
// Variable was previously used as a clock, and is now being set
// Thus a unordered generated clock...
UINFO(8," VarSetAct "<<nodep<<endl);
vscp->circular(true);
}
}
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
//UINFO(8,"ASS "<<nodep<<endl);
m_assignp = nodep;
nodep->iterateChildren(*this);
m_assignp = NULL;
}
virtual void visit(AstActive* nodep, AstNUser*) {
UINFO(8,"ACTIVE "<<nodep<<endl);
m_activep = nodep;
nodep->sensesp()->iterateChildren(*this);
m_activep = NULL;
nodep->iterateChildren(*this);
}
//-----
virtual void visit(AstVar*, AstNUser*) {} // Don't want varrefs under it
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTRUCTORS
GenClkReadVisitor(AstNetlist* nodep) {
m_activep = NULL;
m_assignp = NULL;
m_topModp = NULL;
nodep->accept(*this);
}
virtual ~GenClkReadVisitor() {}
};
//######################################################################
// GenClk class functions
void V3GenClk::genClkAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
GenClkReadVisitor visitor (nodep);
}

35
src/V3GenClk.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Generated Clock Repairs
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3GENCLK_H_
#define _V3GENCLK_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3GenClk {
public:
static void genClkAll(AstNetlist* nodep);
};
#endif // Guard

79
src/V3Global.h Normal file
View File

@ -0,0 +1,79 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Common headers
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3GLOBAL_H_
#define _V3GLOBAL_H_ 1
#include "config.h"
#include <string>
#include "V3Error.h"
#include "V3Options.h"
#include "V3Ast.h"
//======================================================================
// Statics
//######################################################################
// V3 - The top level class for the entire program
class V3Global {
// Globals
AstNetlist* m_rootp; // Root of entire netlist
int m_debugFileNumber; // Number to append to debug files created
bool m_assertWidthsSame; // Tree should have width()==widthMin()
// Options
public:
V3Options opt; // All options; let user see them directly
public:
// CREATORS
V3Global() {
m_rootp = new AstNetlist;
m_debugFileNumber = 0;
m_assertWidthsSame = false;
}
void clear() {
if (m_rootp) m_rootp->deleteTree(); m_rootp=NULL;
}
// ACCESSORS (general)
AstNetlist* rootp() const { return m_rootp; }
bool assertWidthsSame() const { return m_assertWidthsSame; }
// METHODS
void readFiles();
void checkTree() { rootp()->checkTree(); }
void assertWidthsSame(bool flag) { m_assertWidthsSame = flag; }
string debugFilename(const string& nameComment, int newNumber=0) {
++m_debugFileNumber;
if (newNumber) m_debugFileNumber = newNumber;
char digits[100]; sprintf(digits, "%02d", m_debugFileNumber);
return opt.makeDir()+"/"+opt.prefix()+"_"+digits+"_"+nameComment;
}
};
extern V3Global v3Global;
//######################################################################
#endif // guard

321
src/V3Graph.cpp Normal file
View File

@ -0,0 +1,321 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Graph optimizations
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include <vector>
#include <map>
#include <memory>
#include "V3Global.h"
#include "V3File.h"
#include "V3Graph.h"
int V3Graph::s_debug = 0;
int V3Graph::debug() { return max(V3Error::debugDefault(), s_debug); }
//######################################################################
//######################################################################
// Vertices
V3GraphVertex::V3GraphVertex(V3Graph* graphp)
: m_fanout(0), m_color(0), m_rank(0)
{
m_userp = NULL;
verticesPushBack(graphp);
}
void V3GraphVertex::verticesPushBack(V3Graph* graphp) {
m_vertices.pushBack(graphp->m_vertices, this);
}
void V3GraphVertex::unlinkEdges(V3Graph* graphp) {
for (V3GraphEdge* edgep = outBeginp(); edgep; /*BELOW*/) {
V3GraphEdge* nextp = edgep->outNextp();
edgep->unlinkDelete();
edgep = nextp;
}
for (V3GraphEdge* edgep = inBeginp(); edgep; /*BELOW*/) {
V3GraphEdge* nextp = edgep->inNextp();
edgep->unlinkDelete();
edgep = nextp;
}
}
void V3GraphVertex::unlinkDelete(V3Graph* graphp) {
// Delete edges
unlinkEdges(graphp);
// Unlink from vertex list
m_vertices.unlink(graphp->m_vertices, this);
// Delete
delete this; //this=NULL;
}
void V3GraphVertex::rerouteEdges(V3Graph* graphp) {
// Make new edges for each from/to pair
for (V3GraphEdge* iedgep = inBeginp(); iedgep; iedgep=iedgep->inNextp()) {
for (V3GraphEdge* oedgep = outBeginp(); oedgep; oedgep=oedgep->outNextp()) {
new V3GraphEdge (graphp, iedgep->fromp(), oedgep->top(),
min(iedgep->weight(),oedgep->weight()),
iedgep->cutable() && oedgep->cutable());
}
}
// Remove old edges
unlinkEdges(graphp);
}
bool V3GraphVertex::inSize1() const {
return !inEmpty() && inBeginp()->inNextp()==NULL;
}
bool V3GraphVertex::outSize1() const {
return !outEmpty() && outBeginp()->outNextp()==NULL;
}
ostream& operator<<(ostream& os, V3GraphVertex* vertexp) {
os<<" VERTEX="<<vertexp->name();
if (vertexp->rank()) os<<" r"<<vertexp->rank();
if (vertexp->fanout()) os<<" f"<<vertexp->fanout();
if (vertexp->color()) os<<" c"<<vertexp->color();
return os;
}
//######################################################################
//######################################################################
// Edges
V3GraphEdge::V3GraphEdge(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;
m_top = top;
m_weight = weight;
m_cutable = cutable;
m_userp = NULL;
// Link vertices to this edge
outPushBack();
inPushBack();
}
void V3GraphEdge::unlinkDelete() {
// Unlink from side
m_outs.unlink(m_fromp->m_outs, this);
// Unlink to side
m_ins.unlink(m_top->m_ins, this);
// Delete
delete this; //this=NULL;
}
void V3GraphEdge::outPushBack() {
// m_fromp->m_outsp.push_back(this);
m_outs.pushBack(m_fromp->m_outs, this);
}
void V3GraphEdge::inPushBack() {
// m_top->m_insp.push_back(this);
m_ins.pushBack(m_top->m_ins, this);
}
//######################################################################
//######################################################################
// Graph top level
// Constructors
V3Graph::V3Graph() {
// Anything here is probably needed in clear() also
verticesUnlink();
}
V3Graph::~V3Graph() {
clear();
}
void V3Graph::clear() {
// Empty it of all points, as if making a new object
// Delete the old edges
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; /*BELOW*/) {
V3GraphEdge* nextp = edgep->outNextp();
delete edgep;
edgep = nextp;
}
vertexp->outUnlink();
}
// Delete the old vertices
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; /*BELOW*/) {
V3GraphVertex* nextp = vertexp->verticesNextp();
delete vertexp;
vertexp = nextp;
}
verticesUnlink();
}
void V3Graph::userClearVertices() {
// Clear user() in all of tree
// We may use the userCnt trick in V3Ast later... For now we don't call this often, and
// the extra code on each read of user() would probably slow things down more then help.
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
vertexp->user(0);
}
}
void V3Graph::userClearEdges() {
// Clear user() in all of tree
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
edgep->userp(NULL);
}
}
}
void V3Graph::clearColors() {
// Reset colors
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
vertexp->m_color = 0;
}
}
//======================================================================
// Dumping
void V3Graph::loopsVertexCb(V3GraphVertex* vertexp) {
// Needed here as V3GraphVertex<< isn't defined until later in header
cout<<"-Info-Loop: "<<(void*)(vertexp)<<" "<<vertexp<<endl;
}
void V3Graph::dump(ostream& os) {
// This generates a file used by graphviz, http://www.graphviz.org
os<<" Graph:\n";
// Print vertices
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
os<<"\tNode: "<<vertexp->name();
if (vertexp->color()) os<<" color="<<vertexp->color();
os<<endl;
// Print edges
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
dumpEdge (os, vertexp, edgep);
}
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
dumpEdge (os, vertexp, edgep);
}
}
}
void V3Graph::dumpEdge(ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep) {
if (edgep->weight()
&& (edgep->fromp() == vertexp
|| edgep->top() == vertexp)) {
os<<"\t\t";
if (edgep->fromp() == vertexp) os << "-> "<<edgep->top()->name();
if (edgep->top() == vertexp) os << "<- "<<edgep->fromp()->name();
if (edgep->cutable()) os<<" [CUTABLE]";
os<<endl;
}
}
void V3Graph::dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph) {
if (v3Global.opt.dumpTree()) {
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:
const auto_ptr<ofstream> logp (V3File::new_ofstream(filename));
if (logp->fail()) v3fatalSrc("Can't write "<<filename);
// Header
*logp<<"digraph v3graph {\n";
*logp<<"\trankdir="<<dotRankDir()<<"\n";
*logp<<"\tsize="<<"\"7.5,10\""<<"\n";
// List of all possible subgraphs
typedef multimap<string,V3GraphVertex*> SubgraphMmap;
SubgraphMmap subgraphs;
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
string vertexSubgraph = (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : "";
subgraphs.insert(make_pair(vertexSubgraph, vertexp));
}
// We use a map here, as we don't want to corrupt anything (userp) in the graph,
// and we don't care if this is slow.
map<V3GraphVertex*,int> numMap;
// Print vertices
int n=0;
string subgr;
for (SubgraphMmap::iterator it = subgraphs.begin(); it!=subgraphs.end(); ++it) {
string vertexSubgraph = it->first;
V3GraphVertex* vertexp = it->second;
numMap[vertexp] = n;
if (subgr != vertexSubgraph) {
if (subgr!="") *logp<<"\t};\n";
subgr = vertexSubgraph;
if (subgr!="") *logp<<"\tsubgraph cluster_"<<subgr<<" {\n";
}
if (subgr!="") *logp<<"\t";
*logp<<"\tn"<<vertexp->dotName()<<(n++)
<<"\t[fontsize=8 "
<<"label=\""<<(vertexp->name()!="" ? vertexp->name() : "\\N");
if (vertexp->rank()) *logp<<" r"<<vertexp->rank();
if (vertexp->fanout()) *logp<<" f"<<vertexp->fanout();
if (vertexp->color()) *logp<<"\\n c"<<vertexp->color();
*logp<<"\"";
*logp<<", color="<<vertexp->dotColor();
if (vertexp->dotStyle()!="") *logp<<", style="<<vertexp->dotStyle();
if (vertexp->dotShape()!="") *logp<<", shape="<<vertexp->dotShape();
*logp<<"];\n";
}
if (subgr!="") *logp<<"\t};\n";
// Print edges
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (edgep->weight()) {
int fromVnum = numMap[edgep->fromp()];
int toVnum = numMap[edgep->top()];
*logp<<"\tn"<<edgep->fromp()->dotName()<<fromVnum
<<" -> n"<<edgep->top()->dotName()<<toVnum
<<" ["
//<<"fontsize=8 label=\""<<(edgep->name()!="" ? edgep->name() : "\\E")<<"\""
<<"fontsize=8 label=\""<<(edgep->dotLabel()!="" ? edgep->dotLabel() : "")<<"\""
<<" weight="<<edgep->weight()
<<" color="<<edgep->dotColor();
if (edgep->dotStyle()!="") *logp<<" style="<<edgep->dotStyle();
//if (edgep->cutable()) { *logp<<",constraint=false"; } // to rank without following edges
*logp<<"];\n";
}
}
}
// Vertex::m_user end, now unused
// Trailer
*logp << "}\n";
logp->close();
cout << "dot -Tps -o ~/a.ps "<<filename<<endl;
}

240
src/V3Graph.h Normal file
View File

@ -0,0 +1,240 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Graph optimizations
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3GRAPH_H_
#define _V3GRAPH_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3List.h"
#include <vector>
#include <algorithm>
class V3Graph;
class V3GraphVertex;
class V3GraphEdge;
class GraphOrder;
class GraphAcycEdge;
class OrderEitherVertex;
class OrderLogicVertex;
//=============================================================================
// Most graph algorithms accept an arbitrary function that returns
// True for those edges we should honor.
typedef bool (*V3EdgeFuncP)(const V3GraphEdge* edgep);
//============================================================================
class V3Graph {
private:
// STATE
V3List<V3GraphVertex*> m_vertices; // All vertices
static int s_debug;
protected:
friend class V3GraphVertex; friend class V3GraphEdge;
friend class GraphAcyc;
// METHODS
void acyclicDFS();
void acyclicDFSIterate(V3GraphVertex *vertexp, int depth, uint32_t currentRank);
void acyclicCut();
void acyclicLoop(V3GraphVertex* vertexp, int depth);
double orderDFSIterate(V3GraphVertex* vertexp);
void dumpEdge(ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep);
void verticesUnlink() { m_vertices.reset(); }
// ACCESSORS
static int debug();
public:
V3Graph();
virtual ~V3Graph();
static void debug(int level) { s_debug = level; }
virtual string dotRankDir() { return "TB"; } // rankdir for dot plotting
// METHODS
void clear(); // Empty it of all vertices/edges, as if making a new object
void clearColors();
/// Assign same color to all vertices in the same weakly connected component
/// Thus different color if there's no edges between the two subgraphs
void weaklyConnected(V3EdgeFuncP edgeFuncp);
/// Assign same color to all vertices that are strongly connected
/// Thus different color if there's no directional circuit within the subgraphs.
/// (I.E. all loops will occur within each color, not between them.)
void stronglyConnected(V3EdgeFuncP edgeFuncp);
/// Assign a ordering number to all vertexes in a tree.
/// All nodes with no inputs will get rank 1
void rank(V3EdgeFuncP edgeFuncp);
void rank();
/// Sort all vertices by rank and fanout, lowest first
/// Sort all edges by weight, lowest first
void order();
/// Make acyclical (into a tree) by breaking a minimal subset of cutable edges.
void acyclic(V3EdgeFuncP edgeFuncp);
/// Delete any nodes with only outputs
void deleteCutableOnlyEdges();
/// Any cutable edged become non-cutable
void makeEdgesNonCutable(V3EdgeFuncP edgeFuncp);
/// Remove any redundant edges, weights become MAX of any other weight
void removeRedundantEdges(V3EdgeFuncP edgeFuncp);
/// Remove any redundant edges, weights become SUM of any other weight
void removeRedundantEdgesSum(V3EdgeFuncP edgeFuncp);
/// Call loopsVertexCb on any loops starting where specified
void reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp);
/// Debugging
void dump(ostream& os=cout);
void dumpDotFile(const string& filename, bool colorAsSubgraph);
void dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph=false);
void userClearVertices();
void userClearEdges();
static void test();
//
V3GraphVertex* verticesBeginp() const { return m_vertices.begin(); }
// CALLBACKS
virtual void loopsMessageCb(V3GraphVertex* vertexp) { v3fatalSrc("Loops detected in graph: "<<vertexp); }
virtual void loopsVertexCb(V3GraphVertex* vertexp);
};
//============================================================================
class V3GraphVertex : public AstNUser {
// Vertices may be a 'gate'/wire statement OR a variable
protected:
friend class V3Graph; friend class V3GraphEdge;
friend class GraphOrderEdgeCmp; friend class GraphOrderVertexCmp;
friend class GraphAcyc; friend class GraphAlgRank;
V3ListEnt<V3GraphVertex*> m_vertices;// All vertices, linked list
V3List<V3GraphEdge*> m_outs; // Outbound edges,linked list
V3List<V3GraphEdge*> m_ins; // Inbound edges, linked list
double m_fanout; // Order fanout
uint32_t m_color; // Color of the node
uint32_t m_rank; // Rank of edge
union {
void* m_userp; // Marker for some algorithms
uint32_t m_user; // Marker for some algorithms
};
// METHODS
void verticesPushBack(V3Graph* graphp);
// ACCESSORS
void fanout(double fanout) { m_fanout = fanout; }
void rank(uint32_t rank) { m_rank = rank; }
void outUnlink() { m_outs.reset(); }
public:
// CONSTRUCTION
V3GraphVertex(V3Graph* graphp);
virtual ~V3GraphVertex() {}
void unlinkEdges(V3Graph* graphp);
void unlinkDelete(V3Graph* graphp);
// ACCESSORS
virtual string name() const { return ""; }
virtual string dotColor() const { return "black"; }
virtual string dotShape() const { return ""; }
virtual string dotStyle() const { return ""; }
virtual string dotName() const { return ""; }
uint32_t color() const { return m_color; }
void color(uint32_t color) { m_color = color; }
uint32_t rank() const { return m_rank; }
double fanout() const { return m_fanout; }
void user(uint32_t user) { m_user = user; }
uint32_t user() const { return m_user; }
void userp(void* userp) { m_userp = userp; }
void* userp() const { return m_userp; }
// ITERATORS
V3GraphVertex* verticesNextp() const { return m_vertices.nextp(); }
V3GraphEdge* inBeginp() const { return m_ins.begin(); }
bool inEmpty() const { return inBeginp()==NULL; }
bool inSize1() const;
V3GraphEdge* outBeginp() const { return m_outs.begin(); }
bool outEmpty() const { return outBeginp()==NULL; }
bool outSize1() const;
// METHODS
void rerouteEdges(V3Graph* graphp); ///< Edges are routed around this vertex to point from "from" directly to "to"
};
ostream& operator<<(ostream& os, V3GraphVertex* vertexp);
//============================================================================
class V3GraphEdge {
// Wires/variables aren't edges. Edges have only a single to/from vertex
protected:
friend class V3Graph; friend class V3GraphVertex;
friend class GraphOrderEdgeCmp; friend class GraphOrderVertexCmp;
friend class GraphAcyc; friend class GraphAcycEdge;
V3ListEnt<V3GraphEdge*> m_outs; // Next Outbound edge for same vertex (linked list)
V3ListEnt<V3GraphEdge*> m_ins; // Next Inbound edge for same vertex (linked list)
//
V3GraphVertex* m_fromp; // Vertices pointing to this edge
V3GraphVertex* m_top; // Vertices this edge points to
int m_weight; // Weight of the connection
bool m_cutable; // Interconnect may be broken in order sorting
union {
void* m_userp; // Marker for some algorithms
uint32_t m_user; // Marker for some algorithms
};
// METHODS
void cut() { m_weight = 0; } // 0 weight is same as disconnected
void outPushBack();
void inPushBack();
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);
virtual ~V3GraphEdge() {}
// METHODS
virtual string name() const { return m_fromp->name()+"->"+m_top->name(); }
virtual string dotLabel() const { return ""; }
virtual string dotColor() const { return cutable()?"yellowGreen":"red"; }
virtual string dotStyle() const { return cutable()?"dashed":""; }
void unlinkDelete();
// ACCESSORS
int weight() const { return m_weight; }
void weight(int weight) { m_weight=weight; }
bool cutable() const { return m_cutable; }
void cutable(bool cutable) { m_cutable=cutable; }
void userp(void* user) { m_userp = user; }
void* userp() const { return m_userp; }
void user(uint32_t user) { m_user = user; }
uint32_t user() const { return m_user; }
V3GraphVertex* fromp() const { return m_fromp; }
V3GraphVertex* top() const { return m_top; }
// STATIC ACCESSORS
static bool followNotCutable(const V3GraphEdge* edgep) { return !edgep->m_cutable; }
static bool followAlwaysTrue(const V3GraphEdge*) { return true; }
// ITERATORS
V3GraphEdge* outNextp() const { return m_outs.nextp(); }
V3GraphEdge* inNextp() const { return m_ins.nextp(); }
};
//============================================================================
#endif // Guard

559
src/V3GraphAcyc.cpp Normal file
View File

@ -0,0 +1,559 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Graph acyclic algorithm
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include <vector>
#include <list>
#include "V3Global.h"
#include "V3Graph.h"
//######################################################################
//######################################################################
// Algorithms - acyclic
// Break the minimal number of backward edges to make the graph acyclic
class GraphAcycVertex : public V3GraphVertex {
V3GraphVertex* m_origVertexp; // Pointer to first vertex this represents
protected:
friend class GraphAcyc;
V3ListEnt<GraphAcycVertex*> m_work; // List of vertices with optimization work left
uint32_t m_storedRank; // Rank held until commit to edge placement
bool m_onWorkList; // True if already on list of work to do
bool m_deleted; // True if deleted
public:
GraphAcycVertex(V3Graph* graphp, V3GraphVertex* origVertexp)
: V3GraphVertex(graphp), m_origVertexp(origVertexp), m_onWorkList(false), m_deleted(false) {
}
virtual ~GraphAcycVertex() {}
V3GraphVertex* origVertexp() const { return m_origVertexp; }
void setDelete() { m_deleted = true; }
bool isDelete() const { return m_deleted; }
virtual string name() const { return m_origVertexp->name(); }
virtual string dotColor() const { return m_origVertexp->dotColor(); }
};
//--------------------------------------------------------------------
class GraphAcycEdge : public V3GraphEdge {
// userp() is always used to point to the head original graph edge
private:
typedef list<V3GraphEdge*> OrigEdgeList; // List of orig edges, see also GraphAcyc's decl
V3GraphEdge* origEdgep() const {
OrigEdgeList* oEListp = ((OrigEdgeList*)userp());
if (!oEListp) v3fatalSrc("No original edge associated with acyc edge "<<this<<endl);
return (oEListp->front());
}
public:
GraphAcycEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cutable=false)
: V3GraphEdge(graphp, fromp, top, weight, cutable) {
}
virtual ~GraphAcycEdge() {}
// yellow=we might still cut it, else oldEdge: yellowGreen=made uncutable, red=uncutable
virtual string dotColor() const { return (cutable()?"yellow":origEdgep()->dotColor()); }
};
//--------------------------------------------------------------------
struct GraphAcycEdgeCmp {
inline bool operator () (const V3GraphEdge* lhsp, const V3GraphEdge* rhsp) const {
if (lhsp->weight() > rhsp->weight()) return 1; // LHS goes first
if (lhsp->weight() < rhsp->weight()) return 0; // RHS goes first
return 0;
}
};
//--------------------------------------------------------------------
// CLASSES
class GraphAcyc {
private:
typedef list<V3GraphEdge*> OrigEdgeList; // List of orig edges, see also GraphAcycEdge's decl
// GRAPH USERS
// origGraph
// GraphVertex::user() GraphAycVerted* New graph node
// m_breakGraph
// GraphEdge::user() OrigEdgeList* Old graph edges
// GraphVertex::user bool Detection of loops in simplifyDupIterate
// MEMBERS
V3Graph m_breakGraph; // Graph with only breakable edges represented
V3List<GraphAcycVertex*> m_work; // List of vertices with optimization work left
vector<OrigEdgeList*> m_origEdgeDelp; // List of deletions to do when done
V3EdgeFuncP m_origEdgeFuncp; // Function that says we follow this edge (in original graph)
uint32_t m_placeStep; // Number that user() must be equal to to indicate processing
int debug() { return V3Graph::debug(); }
// METHODS
void buildGraph (V3Graph* origGraphp);
void buildGraphIterate (V3GraphVertex* overtexp, GraphAcycVertex* avertexp);
void simplify (bool allowCut);
void simplifyNone (GraphAcycVertex* vertexp);
void simplifyOne (GraphAcycVertex* vertexp);
void simplifyOut (GraphAcycVertex* vertexp);
void simplifyDup (GraphAcycVertex* vertexp);
void cutBasic (GraphAcycVertex* vertexp);
void cutBackward (GraphAcycVertex* vertexp);
void deleteMarked();
void place();
void placeTryEdge(V3GraphEdge* edgep);
bool placeIterate(GraphAcycVertex* vertexp, uint32_t currentRank);
inline bool origFollowEdge(V3GraphEdge* edgep) {
return (edgep->weight() && (m_origEdgeFuncp)(edgep));
}
V3GraphEdge* edgeFromEdge (V3GraphEdge* oldedgep, V3GraphVertex* fromp, V3GraphVertex* top) {
// Make new breakGraph edge, with old edge as a template
GraphAcycEdge* newEdgep = new GraphAcycEdge (&m_breakGraph, fromp, top,
oldedgep->weight(), oldedgep->cutable());
newEdgep->userp(oldedgep->userp()); // Keep pointer to OrigEdgeList
return newEdgep;
}
void addOrigEdgep (V3GraphEdge* toEdgep, V3GraphEdge* addEdgep) {
// Add addEdge (or it's list) to list of edges that break edge represents
// Note addEdge may already have a bunch of similar linked edge representations. Yuk.
UASSERT(addEdgep, "Adding NULL");
if (!toEdgep->userp()) {
OrigEdgeList* oep = new OrigEdgeList;
m_origEdgeDelp.push_back(oep);
toEdgep->userp(oep);
}
OrigEdgeList* oEListp = (OrigEdgeList*)(toEdgep->userp());
if (OrigEdgeList* addListp = (OrigEdgeList*)(addEdgep->userp())) {
for (OrigEdgeList::iterator it = addListp->begin(); it != addListp->end(); ++it) {
oEListp->push_back(*it);
}
addListp->clear(); // Done with it
} else {
oEListp->push_back(addEdgep);
}
}
void cutOrigEdge (V3GraphEdge* breakEdgep, const char* why) {
// From the break edge, cut edges in original graph it represents
UINFO(8,why<<" CUT "<<breakEdgep->fromp()<<endl);
breakEdgep->cut();
OrigEdgeList* oEListp = (OrigEdgeList*)(breakEdgep->userp());
if (!oEListp) v3fatalSrc("No original edge associated with cutting edge "<<breakEdgep<<endl);
// The breakGraph edge may represent multiple real edges; cut them all
for (OrigEdgeList::iterator it = oEListp->begin(); it != oEListp->end(); ++it) {
V3GraphEdge* origEdgep = *it;
origEdgep->cut();
UINFO(8," "<<why<<" "<<origEdgep->fromp()<<" ->"<<origEdgep->top()<<endl);
}
}
// Work Que
void workPush(V3GraphVertex* vertexp) {
GraphAcycVertex* avertexp = (GraphAcycVertex*)vertexp;
// Add vertex to list of nodes needing further optimization trials
if (!avertexp->m_onWorkList) {
avertexp->m_onWorkList = true;
avertexp->m_work.pushBack(m_work, avertexp);
}
}
GraphAcycVertex* workBeginp() { return m_work.begin(); }
void workPop() {
GraphAcycVertex* avertexp = workBeginp();
avertexp->m_onWorkList = false;
avertexp->m_work.unlink(m_work, avertexp); }
public:
// CONSTRUCTORS
GraphAcyc(V3EdgeFuncP edgeFuncp) {
m_origEdgeFuncp = edgeFuncp;
}
~GraphAcyc() {
for (vector<OrigEdgeList*>::iterator it = m_origEdgeDelp.begin(); it != m_origEdgeDelp.end(); ++it) {
delete (*it);
}
m_origEdgeDelp.clear();
}
void main(V3Graph* origGraphp);
};
//--------------------------------------------------------------------
void GraphAcyc::buildGraph (V3Graph* origGraphp) {
// Presumes the graph has been strongly ordered,
// and thus there's a unique color if there are loops in this subgraph.
// For each old node, make a new graph node for optimization
origGraphp->userClearVertices();
origGraphp->userClearEdges();
for (V3GraphVertex* overtexp = origGraphp->verticesBeginp(); overtexp; overtexp=overtexp->verticesNextp()) {
if (overtexp->color()) {
GraphAcycVertex* avertexp = new GraphAcycVertex(&m_breakGraph, overtexp);
overtexp->userp(avertexp); // Stash so can look up later
}
}
// Build edges between logic vertices
for (V3GraphVertex* overtexp = origGraphp->verticesBeginp(); overtexp; overtexp=overtexp->verticesNextp()) {
if (overtexp->color()) {
GraphAcycVertex* avertexp = (GraphAcycVertex*)(overtexp->userp());
buildGraphIterate(overtexp, avertexp);
}
}
}
void GraphAcyc::buildGraphIterate (V3GraphVertex* overtexp, GraphAcycVertex* avertexp) {
// Make new edges
for (V3GraphEdge* edgep = overtexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (origFollowEdge(edgep)) { // not cut
V3GraphVertex* toVertexp = edgep->top();
if (toVertexp->color()) {
GraphAcycVertex* toAVertexp = (GraphAcycVertex*)(toVertexp->userp());
// Replicate the old edge into the new graph
// There may be multiple edges between same pairs of vertices
V3GraphEdge* breakEdgep = new GraphAcycEdge
(&m_breakGraph, avertexp, toAVertexp, edgep->weight(), edgep->cutable());
addOrigEdgep (breakEdgep, edgep); // So can find original edge
}
}
}
}
void GraphAcyc::simplify (bool allowCut) {
// Add all nodes to list of work to do
for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
workPush(vertexp);
}
// Optimize till everything finished
while (GraphAcycVertex* vertexp = workBeginp()) {
workPop();
simplifyNone(vertexp);
simplifyOne(vertexp);
simplifyOut(vertexp);
simplifyDup(vertexp);
if (allowCut) {
// The main algorithm works without these, though slower
// So if changing the main algorithm, comment these out for a test run
if (v3Global.opt.oAcycSimp()) {
cutBasic(vertexp);
cutBackward(vertexp);
}
}
}
deleteMarked();
}
void GraphAcyc::deleteMarked () {
// Delete nodes marked for removal
for (V3GraphVertex* nextp, *vertexp = m_breakGraph.verticesBeginp(); vertexp; vertexp=nextp) {
nextp = vertexp->verticesNextp();
GraphAcycVertex* avertexp = (GraphAcycVertex*)vertexp;
if (avertexp->isDelete()) {
avertexp->unlinkDelete(&m_breakGraph); avertexp=NULL;
}
}
}
void GraphAcyc::simplifyNone (GraphAcycVertex* avertexp) {
// Don't need any vertices with no inputs, There's no way they can have a loop.
// Likewise, vertices with no outputs
if (avertexp->isDelete()) return;
if (avertexp->inEmpty() || avertexp->outEmpty()) {
UINFO(9," SimplifyNoneRemove "<<avertexp<<endl);
avertexp->setDelete(); // Mark so we won't delete it twice
// Remove edges
while (V3GraphEdge* edgep = avertexp->outBeginp()) {
V3GraphVertex* otherVertexp = edgep->top();
//UINFO(9," out "<<otherVertexp<<endl);
edgep->unlinkDelete(); edgep = NULL;
workPush(otherVertexp);
}
while (V3GraphEdge* edgep = avertexp->inBeginp()) {
V3GraphVertex* otherVertexp = edgep->fromp();
//UINFO(9," in "<<otherVertexp<<endl);
edgep->unlinkDelete(); edgep = NULL;
workPush(otherVertexp);
}
}
}
void GraphAcyc::simplifyOne (GraphAcycVertex* avertexp) {
// If a node has one input and one output, we can remove it and change the edges
if (avertexp->isDelete()) return;
if (avertexp->inSize1() && avertexp->outSize1()) {
V3GraphEdge* inEdgep = avertexp->inBeginp();
V3GraphEdge* outEdgep = avertexp->outBeginp();
V3GraphVertex* inVertexp = inEdgep->fromp();
V3GraphVertex* outVertexp = outEdgep->top();
// The in and out may be the same node; we'll make a loop
// The in OR out may be THIS node; we can't delete it then.
if (inVertexp!=avertexp && outVertexp!=avertexp) {
UINFO(9," SimplifyOneRemove "<<avertexp<<endl);
avertexp->setDelete(); // Mark so we won't delete it twice
// Make a new edge connecting the two vertices directly
// If both are breakable, we pick the one with less weight, else it's arbitrary
V3GraphEdge* templateEdgep = ( (inEdgep->cutable()
&& (!outEdgep->cutable()
|| inEdgep->weight()<outEdgep->weight() ))
? inEdgep : outEdgep);
edgeFromEdge(templateEdgep, inVertexp, outVertexp);
// Remove old edge
inEdgep->unlinkDelete(); inEdgep = NULL;
outEdgep->unlinkDelete(); outEdgep = NULL; templateEdgep=NULL;
workPush(inVertexp);
workPush(outVertexp);
}
}
}
void GraphAcyc::simplifyOut (GraphAcycVertex* avertexp) {
// If a node has one output that's not cutable, all its inputs can be reassigned
// to the next node in the list
if (avertexp->isDelete()) return;
if (avertexp->outSize1()) {
V3GraphEdge* outEdgep = avertexp->outBeginp();
if (!outEdgep->cutable()) {
V3GraphVertex* outVertexp = outEdgep->top();
UINFO(9," SimplifyOutRemove "<<avertexp<<endl);
avertexp->setDelete(); // Mark so we won't delete it twice
for (V3GraphEdge* nextp, *inEdgep = avertexp->inBeginp(); inEdgep; inEdgep=nextp) {
nextp = inEdgep->inNextp();
V3GraphVertex* inVertexp = inEdgep->fromp();
if (inVertexp == avertexp) v3fatalSrc("Non-cutable edge forms a loop "<<avertexp);
// Make a new edge connecting the two vertices directly
edgeFromEdge(inEdgep, inVertexp, outVertexp);
// Remove old edge
inEdgep->unlinkDelete(); inEdgep = NULL;
workPush(inVertexp);
}
outEdgep->unlinkDelete(); outEdgep = NULL;
workPush(outVertexp);
}
}
}
void GraphAcyc::simplifyDup (GraphAcycVertex* avertexp) {
// Remove redundant edges
if (avertexp->isDelete()) return;
// Clear marks
for (V3GraphEdge* edgep = avertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
edgep->top()->user(false);
}
// Mark edges and detect duplications
for (V3GraphEdge* nextp, *edgep = avertexp->outBeginp(); edgep; edgep=nextp) {
nextp = edgep->outNextp();
V3GraphVertex* outVertexp = edgep->top();
V3GraphEdge* prevEdgep = (V3GraphEdge*)outVertexp->userp();
if (prevEdgep) {
if (!prevEdgep->cutable()) {
// !cutable duplicates prev !cutable: we can ignore it, redundant
// cutable duplicates prev !cutable: know it's not a relevant loop, ignore it
UINFO(8," DelDupEdge "<<avertexp<<endl);
edgep->unlinkDelete(); edgep = NULL;
} else if (!edgep->cutable()) {
// !cutable duplicates prev cutable: delete the earlier cutable
UINFO(8," DelDupPrev "<<avertexp<<endl);
prevEdgep->unlinkDelete(); prevEdgep = NULL;
outVertexp->userp(edgep);
} else {
// cutable duplicates prev cutable: combine weights
UINFO(8," DelDupComb "<<avertexp<<endl);
prevEdgep->weight (prevEdgep->weight() + edgep->weight());
addOrigEdgep (prevEdgep, edgep);
edgep->unlinkDelete(); edgep = NULL;
}
workPush(outVertexp);
workPush(avertexp);
} else {
// No previous assignment
outVertexp->userp(edgep);
}
}
}
void GraphAcyc::cutBasic (GraphAcycVertex* avertexp) {
// Detect and cleanup any loops from node to itself
if (avertexp->isDelete()) return;
for (V3GraphEdge* nextp, *edgep = avertexp->outBeginp(); edgep; edgep=nextp) {
nextp = edgep->outNextp();
if (edgep->cutable() && edgep->top()==avertexp) {
cutOrigEdge (edgep, " Cut Basic");
edgep->unlinkDelete(); edgep = NULL;
workPush(avertexp);
}
}
}
void GraphAcyc::cutBackward (GraphAcycVertex* avertexp) {
// If a cutable edge is from A->B, and there's a non-cutable edge B->A, then must cut!
if (avertexp->isDelete()) return;
// Clear marks
for (V3GraphEdge* edgep = avertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
edgep->top()->user(false);
}
for (V3GraphEdge* edgep = avertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
if (!edgep->cutable()) edgep->fromp()->user(true);
}
// Detect duplications
for (V3GraphEdge* nextp, *edgep = avertexp->outBeginp(); edgep; edgep=nextp) {
nextp = edgep->outNextp();
if (edgep->cutable() && edgep->top()->user()) {
cutOrigEdge (edgep, " Cut A->B->A");
edgep->unlinkDelete(); edgep = NULL;
workPush(avertexp);
}
}
}
void GraphAcyc::place() {
// Input is m_breakGraph with ranks already assigned on non-breakable edges
// Make a list of all cutable edges in the graph
int numEdges = 0;
for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (edgep->weight() && edgep->cutable()) {
numEdges++;
}
}
}
UINFO(4, " Cutable edges = "<<numEdges<<endl);
vector<V3GraphEdge*> edges; // List of all edges to be processed
edges.reserve(numEdges+1); // Make the vector properly sized right off the bat -- faster then reallocating
for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
vertexp->user(0); // Clear in prep of next step
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (edgep->weight() && edgep->cutable()) {
edges.push_back(edgep);
}
}
}
// Sort by weight, then by vertex (so that we completely process one vertex, when possible)
sort(edges.begin(), edges.end(), GraphAcycEdgeCmp());
// Process each edge in weighted order
m_placeStep = 10;
for (vector<V3GraphEdge*>::iterator it = edges.begin(); it!=edges.end(); ++it) {
V3GraphEdge* edgep = (*it);
placeTryEdge(edgep);
}
}
void GraphAcyc::placeTryEdge(V3GraphEdge* edgep) {
// Try to make this edge uncutable
m_placeStep++;
UINFO(8, " PlaceEdge s"<<m_placeStep<<" w"<<edgep->weight()<<" "<<edgep->fromp()<<endl);
// Make the edge uncutable so we detect it in placement
edgep->cutable(false);
// Vertex::m_user begin: number indicates this edge was completed
// Try to assign ranks, presuming this edge is in place
// If we come across user()==placestep, we've detected a loop and must back out
bool loop=placeIterate((GraphAcycVertex*)edgep->top(), edgep->fromp()->rank()+1);
if (!loop) {
// No loop, we can keep it as uncutable
// Commit the new ranks we calculated
// Just cleanup the list. If this is slow, we can add another set of
// user counters to avoid cleaning up the list.
while (workBeginp()) {
workPop();
}
} else {
// Adding this edge would cause a loop, kill it
edgep->cutable(true); // So graph still looks pretty
cutOrigEdge (edgep, " Cut loop");
edgep->unlinkDelete(); edgep = NULL;
// Backout the ranks we calculated
while (GraphAcycVertex* vertexp = workBeginp()) {
workPop();
vertexp->rank(vertexp->m_storedRank);
}
}
}
bool GraphAcyc::placeIterate(GraphAcycVertex* vertexp, uint32_t currentRank) {
// Assign rank to each unvisited node
// rank() is the "committed rank" of the graph known without loops
// If larger rank is found, assign it and loop back through
// If we hit a back node make a list of all loops
if (vertexp->rank() >= currentRank) return false; // Already processed it
if (vertexp->user() == m_placeStep) return true; // Loop detected
vertexp->user(m_placeStep);
// Remember we're changing the rank of this node; might need to back out
if (!vertexp->m_onWorkList) {
vertexp->m_storedRank = vertexp->rank();
workPush(vertexp);
}
vertexp->rank(currentRank);
// Follow all edges and increase their ranks
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (edgep->weight() && !edgep->cutable()) {
if (placeIterate((GraphAcycVertex*)edgep->top(), currentRank+1)) {
// We don't need to reset user(); we'll use a different placeStep for the next edge
return true; // Loop detected
}
}
}
vertexp->user(0);
return false;
}
//----- Main algorithm entry point
void GraphAcyc::main (V3Graph* origGraphp) {
m_breakGraph.userClearEdges();
// Color based on possible loops
origGraphp->stronglyConnected(m_origEdgeFuncp);
// Make a new graph with vertices that have only a single vertex
// for each group of old vertices that are interconnected with unbreakable
// edges (and thus can't represent loops - if we did the unbreakable
// marking right, anyways)
buildGraph (origGraphp);
if (debug()>=6) m_breakGraph.dumpDotFilePrefixed("acyc_pre");
// Perform simple optimizations before any cuttings
simplify(false);
if (debug()>=5) m_breakGraph.dumpDotFilePrefixed("acyc_simp");
UINFO(4, " Cutting trivial loops\n");
simplify(true);
if (debug()>=6) m_breakGraph.dumpDotFilePrefixed("acyc_mid");
UINFO(4, " Ranking\n");
m_breakGraph.rank(&V3GraphEdge::followNotCutable);
if (debug()>=6) m_breakGraph.dumpDotFilePrefixed("acyc_rank");
UINFO(4, " Placement\n");
place();
if (debug()>=6) m_breakGraph.dumpDotFilePrefixed("acyc_place");
UINFO(4, " Final Ranking\n");
// Only needed to assert there are no loops in completed graph
m_breakGraph.rank(&V3GraphEdge::followAlwaysTrue);
if (debug()>=6) m_breakGraph.dumpDotFilePrefixed("acyc_done");
}
void V3Graph::acyclic(V3EdgeFuncP edgeFuncp) {
UINFO(4, "Acyclic\n");
GraphAcyc acyc (edgeFuncp);
acyc.main(this);
UINFO(4, "Acyclic done\n");
}

482
src/V3GraphAlg.cpp Normal file
View File

@ -0,0 +1,482 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Graph optimizations
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include <vector>
#include <map>
#include <list>
#include "V3Global.h"
#include "V3GraphAlg.h"
//######################################################################
//######################################################################
// Algorithms - delete
void V3Graph::deleteCutableOnlyEdges() {
// Any vertices with only cutable edges will get deleted
// Vertex::m_user begin: indicates can be deleted
// Pass 1, mark those. Don't delete now, as we don't want to rip out whole trees
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
vertexp->user(true);
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
if (!edgep->cutable()) {
vertexp->user(false); // Can't delete it
break;
}
}
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (!edgep->cutable()) {
vertexp->user(false); // Can't delete it
break;
}
}
}
// Pass 2, delete those marked
// Rather then doing a delete() we set the weight to 0 which disconnects the edge.
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (vertexp->user()) {
//UINFO(7,"Disconnect "<<vertexp->name()<<endl);
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
edgep->cut();
}
}
}
// Vertex::m_user end, now unused
}
//######################################################################
//######################################################################
// Algorithms - weakly connected components
class GraphRemoveRedundant : GraphAlg {
bool m_sumWeights; ///< Sum, rather then maximize weights
private:
void main() {
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
vertexIterate(vertexp);
}
}
void vertexIterate(V3GraphVertex* vertexp) {
// Clear marks
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
edgep->top()->user(false);
}
// Mark edges and detect duplications
for (V3GraphEdge* nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
nextp = edgep->outNextp();
if (followEdge(edgep)) {
V3GraphVertex* outVertexp = edgep->top();
V3GraphEdge* prevEdgep = (V3GraphEdge*)outVertexp->userp();
if (!prevEdgep) { // No previous assignment
outVertexp->userp(edgep);
} else { // Duplicate
bool saveOld = true;
if (prevEdgep->cutable() && !edgep->cutable()) {
saveOld = false; // new !cutable more important then old
} else if (!prevEdgep->cutable() && edgep->cutable()) {
saveOld = true; // old !cutable more important then new
} else {
saveOld = true;
if (!m_sumWeights && (prevEdgep->weight() < edgep->weight())) { // Keep max weight
prevEdgep->weight(edgep->weight());
}
}
if (saveOld) {
if (m_sumWeights) prevEdgep->weight(prevEdgep->weight() + edgep->weight());
edgep->unlinkDelete(); edgep = NULL;
} else {
if (m_sumWeights) edgep->weight(prevEdgep->weight() + edgep->weight());
prevEdgep->unlinkDelete(); prevEdgep = NULL;
outVertexp->userp(edgep);
}
}
}
}
}
public:
GraphRemoveRedundant(V3Graph* graphp, V3EdgeFuncP edgeFuncp, bool sumWeights)
: GraphAlg(graphp, edgeFuncp), m_sumWeights(sumWeights) {
main();
}
~GraphRemoveRedundant() {}
};
void V3Graph::removeRedundantEdges(V3EdgeFuncP edgeFuncp) {
GraphRemoveRedundant (this, edgeFuncp, false);
}
void V3Graph::removeRedundantEdgesSum(V3EdgeFuncP edgeFuncp) {
GraphRemoveRedundant (this, edgeFuncp, true);
}
//######################################################################
//######################################################################
// Algorithms - weakly connected components
class GraphAlgWeakly : GraphAlg {
private:
void main() {
// Initialize state
m_graphp->clearColors();
// Color graph
uint32_t currentColor = 0;
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
currentColor ++;
vertexIterate(vertexp, currentColor);
}
}
void vertexIterate(V3GraphVertex* vertexp, uint32_t currentColor) {
// Assign new color to each unvisited node
// then visit each of its edges, giving them the same color
if (vertexp->color()) return; // Already colored it
vertexp->color(currentColor);
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (followEdge(edgep)) {
vertexIterate(edgep->top(), currentColor);
}
}
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
if (followEdge(edgep)) {
vertexIterate(edgep->fromp(), currentColor);
}
}
}
public:
GraphAlgWeakly(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: GraphAlg(graphp, edgeFuncp) {
main();
}
~GraphAlgWeakly() {}
};
void V3Graph::weaklyConnected(V3EdgeFuncP edgeFuncp) {
GraphAlgWeakly (this, edgeFuncp);
}
//######################################################################
//######################################################################
// Algorithms - strongly connected components
class GraphAlgStrongly : GraphAlg {
private:
uint32_t m_currentDfs; // DFS count
vector<V3GraphVertex*> m_callTrace; // List of everything we hit processing so far
void main() {
// Use Tarjan's algorithm to find the strongly connected subgraphs.
// Node State:
// Vertex::user // DFS number indicating possible root of subtree, 0=not iterated
// Vertex::color // Output subtree number (fully processed)
// Clear info
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
vertexp->color(0);
vertexp->user(0);
}
// Color graph
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (!vertexp->user()) {
m_currentDfs++;
vertexIterate(vertexp);
}
}
// If there's a single vertex of a color, it doesn't need a subgraph
// This simplifies the consumer's code, and reduces graph debugging clutter
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
bool onecolor = true;
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (followEdge(edgep)) {
if (vertexp->color() == edgep->top()->color()) {
onecolor = false;
break;
}
}
}
if (onecolor) vertexp->color(0);
}
}
void vertexIterate(V3GraphVertex* vertexp) {
uint32_t thisDfsNum = m_currentDfs++;
vertexp->user(thisDfsNum);
vertexp->color(0);
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (followEdge(edgep)) {
V3GraphVertex* top = edgep->top();
if (!top->user()) { // Dest not computed yet
vertexIterate(top);
}
if (!top->color()) { // Dest not in a component
if (vertexp->user() > top->user()) vertexp->user(top->user());
}
}
}
if (vertexp->user() == thisDfsNum) { // New head of subtree
vertexp->color(thisDfsNum); // Mark as component
while (!m_callTrace.empty()) {
V3GraphVertex* popVertexp = m_callTrace.back();
if (popVertexp->user() >= thisDfsNum) { // Lower node is part of this subtree
m_callTrace.pop_back();
popVertexp->color(thisDfsNum);
} else {
break;
}
}
} else { // In another subtree (maybe...)
m_callTrace.push_back(vertexp);
}
}
public:
GraphAlgStrongly(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: GraphAlg(graphp, edgeFuncp) {
m_currentDfs = 0;
main();
}
~GraphAlgStrongly() {}
};
void V3Graph::stronglyConnected(V3EdgeFuncP edgeFuncp) {
GraphAlgStrongly (this, edgeFuncp);
}
//######################################################################
//######################################################################
// Algorithms - ranking
class GraphAlgRank : GraphAlg {
private:
void main() {
// Rank each vertex, ignoring cutable edges
// Vertex::m_user begin: 1 indicates processing, 2 indicates completed
// Clear existing ranks
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
vertexp->rank(0);
vertexp->user(0);
}
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (!vertexp->user()) {
vertexIterate(vertexp,1);
}
}
}
void vertexIterate(V3GraphVertex* vertexp, uint32_t currentRank) {
// Assign rank to each unvisited node
// If larger rank is found, assign it and loop back through
// If we hit a back node make a list of all loops
if (vertexp->user() == 1) {
m_graphp->reportLoops(m_edgeFuncp, vertexp);
m_graphp->loopsMessageCb(vertexp);
return;
}
if (vertexp->rank() >= currentRank) return; // Already processed it
vertexp->user(1);
vertexp->rank(currentRank);
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (followEdge(edgep)) {
vertexIterate(edgep->top(),currentRank+1);
}
}
vertexp->user(2);
}
public:
GraphAlgRank(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: GraphAlg(graphp, edgeFuncp) {
main();
}
~GraphAlgRank() {}
};
void V3Graph::rank() {
GraphAlgRank (this, &V3GraphEdge::followAlwaysTrue);
}
void V3Graph::rank(V3EdgeFuncP edgeFuncp) {
GraphAlgRank (this, edgeFuncp);
}
//######################################################################
//######################################################################
// Algorithms - ranking
class GraphAlgRLoops : GraphAlg {
private:
vector<V3GraphVertex*> m_callTrace; // List of everything we hit processing so far
bool m_done; // Exit algorithm
void main(V3GraphVertex* vertexp) {
// Vertex::m_user begin: 1 indicates processing, 2 indicates completed
// Clear existing ranks
m_graphp->userClearVertices();
m_callTrace.reserve(100);
vertexIterate(vertexp, 0);
}
void vertexIterate(V3GraphVertex* vertexp, uint32_t currentRank) {
// Assign rank to each unvisited node
// When we hit ourself again, return the list of all loops
if (m_done) return;
m_callTrace.reserve(currentRank+10); // Leave slop for speed
m_callTrace[currentRank++] = vertexp;
if (vertexp->user() == 1) {
for (unsigned i=0; i<currentRank; i++) {
m_graphp->loopsVertexCb(m_callTrace[i]);
}
m_done = true;
return;
}
if (vertexp->user() == 2) return; // Already processed it
vertexp->user(1);
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (followEdge(edgep)) {
vertexIterate(edgep->top(),currentRank);
}
}
vertexp->user(2);
}
public:
GraphAlgRLoops(V3Graph* graphp, V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp)
: GraphAlg(graphp, edgeFuncp) {
m_done = false;
main(vertexp);
}
~GraphAlgRLoops() {}
};
void V3Graph::reportLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp) {
GraphAlgRLoops (this, edgeFuncp, vertexp);
}
//######################################################################
//######################################################################
// Algorithms - make non cutable
void V3Graph::makeEdgesNonCutable(V3EdgeFuncP edgeFuncp) {
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
// Only need one direction, we'll always see the other at some point...
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
if (edgep->cutable() && edgep->weight() && (edgeFuncp)(edgep)) {
edgep->cutable(false);
}
}
}
}
//######################################################################
//######################################################################
// Algorithms - ordering
// Compute near optimal ordering of the nodes, where:
// If a required edge is A->B, rank(A)<rank(B)
// Visit edges and assign ranks to keep minimal crossings
// (Results in better dcache packing.)
struct GraphOrderVertexCmp {
inline bool operator () (const V3GraphVertex* lhsp, const V3GraphVertex* rhsp) const {
// LHS goes first if of lower rank, or lower fanout
if (lhsp->m_rank < rhsp->m_rank) return 1;
if (lhsp->m_rank > rhsp->m_rank) return 0;
return (lhsp->m_fanout < rhsp->m_fanout);
}
};
struct GraphOrderEdgeCmp {
inline bool operator () (const V3GraphEdge* lhsp, const V3GraphEdge* rhsp) const {
if (!lhsp->m_weight || !rhsp->m_weight) return 0;
GraphOrderVertexCmp cmp;
return (cmp(lhsp->m_top, rhsp->m_top));
}
};
//--------------------------------------------------------------------
void V3Graph::order() {
UINFO(2,"Order:\n");
// Compute rankings again
rank(&V3GraphEdge::followAlwaysTrue);
// Compute fanouts
// Vertex::m_user begin: 1 indicates processing, 2 indicates completed
userClearVertices();
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (!vertexp->user()) {
orderDFSIterate(vertexp);
}
}
// Speed up subsequent accesses.
{ // Sort list of vertices by rank, then fanout
vector<V3GraphVertex*> vertices;
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
vertices.push_back(vertexp);
}
sort(vertices.begin(), vertices.end(), GraphOrderVertexCmp());
this->verticesUnlink();
for (vector<V3GraphVertex*>::iterator it = vertices.begin(); it!=vertices.end(); ++it) {
(*it)->verticesPushBack(this);
}
}
// Sort edges by rank then fanout of node they point to
vector<V3GraphEdge*> edges;
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
// Make a vector
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
edges.push_back(edgep);
}
// Sort
sort(edges.begin(), edges.end(), GraphOrderEdgeCmp());
// Extract
vertexp->outUnlink();
for (vector<V3GraphEdge*>::iterator it = edges.begin(); it!=edges.end(); ++it) {
(*it)->outPushBack();
}
// Prep for next
edges.clear();
}
}
double V3Graph::orderDFSIterate(V3GraphVertex* vertexp) {
// Compute fanouts of each node
// If forward edge, don't double count that fanout
if (vertexp->user() == 2) return vertexp->fanout(); // Already processed it
if (vertexp->user() == 1) v3fatalSrc("Loop found, backward edges should be dead\n");
vertexp->user(1);
double fanout = 0;
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
if (edgep->weight()) fanout += orderDFSIterate(edgep->m_top);
}
// Just count inbound edges
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
if (edgep->weight()) fanout ++;
}
vertexp->fanout(fanout);
vertexp->user(2);
return vertexp->fanout();
}

48
src/V3GraphAlg.h Normal file
View File

@ -0,0 +1,48 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Graph algorithm base class
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3GRAPHALG_H_
#define _V3GRAPHALG_H_ 1
#include "config.h"
#include "V3Global.h"
#include "V3Graph.h"
//=============================================================================
// Algorithms - common class
// For internal use, most graph algorithms use this as a base class
class GraphAlg {
protected:
V3Graph* m_graphp; // Graph we're operating upon
V3EdgeFuncP m_edgeFuncp; // Function that says we follow this edge
inline bool followEdge(V3GraphEdge* edgep) {
return (edgep->weight() && (m_edgeFuncp)(edgep));
}
GraphAlg(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: m_graphp(graphp), m_edgeFuncp(edgeFuncp) {}
~GraphAlg() {}
};
//============================================================================
#endif // Guard

481
src/V3GraphDfa.cpp Normal file
View File

@ -0,0 +1,481 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Graph optimizations
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2005-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <stack>
#include <map>
#include <set>
#include "V3Global.h"
#include "V3GraphDfa.h"
#include "V3GraphAlg.h"
//######################################################################
//######################################################################
// Algorithms - find starting node
DfaVertex* DfaGraph::findStart() {
DfaVertex* startp = NULL;
for (V3GraphVertex* vertexp = this->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
if (vvertexp->start()) {
if (startp) v3fatalSrc("Multiple start points in NFA graph");
startp = vvertexp;
}
} else {
v3fatalSrc("Non DfaVertex in DfaGraph\n");
}
}
if (!startp) v3fatalSrc("No start point in NFA graph");
return startp;
}
//######################################################################
//######################################################################
// Algorithms - convert NFA to a DFA
// Uses the Subset Construction Algorithm
class GraphNfaToDfa : GraphAlg {
// We have two types of nodes in one graph, NFA and DFA nodes.
// Edges from NFA to NFA come from the user, and indicate input or epsilon transitions
// Edges from DFA to NFA indicate the NFA from which that DFA was formed.
// Edges from DFA to DFA indicate a completed input transition
private:
// TYPES
typedef deque<DfaVertex*> DfaStates;
typedef multimap<uint64_t,DfaVertex*> HashMap;
// MEMBERS
uint32_t m_step; // Processing step, so we can avoid clearUser all the time
HashMap m_hashMap; // Dfa Vertex for each set of NFA vertexes
int debug() { return 0; }
// METHODS
DfaGraph* graphp() { return static_cast<DfaGraph*>(m_graphp); }
bool nfaState(V3GraphVertex* vertexp) { return vertexp->color()==0; }
bool dfaState(V3GraphVertex* vertexp) { return vertexp->color()==1; }
void nextStep() { m_step++; }
bool unseenNfaThisStep(V3GraphVertex* vertexp) {
// A nfa node not already seen this processing step
return (nfaState(vertexp) && !(vertexp->user()==m_step));
}
DfaVertex* newDfaVertex(DfaVertex* nfaTemplatep=NULL) {
DfaVertex* vertexp = new DfaVertex (graphp());
vertexp->color(1); // Mark as dfa
if (nfaTemplatep && nfaTemplatep->start()) vertexp->start(true);
if (nfaTemplatep && nfaTemplatep->accepting()) vertexp->accepting(true);
UINFO(9, " New "<<vertexp<<endl);
return vertexp;
}
// Hashing
uint32_t hashVertex(V3GraphVertex* vertexp) {
union { void* up; struct {uint32_t upper; uint32_t lower;} l;} u;
u.l.upper=0; u.l.lower=0; u.up=vertexp;
return u.l.upper ^ u.l.lower;
}
uint32_t hashDfaOrigins(DfaVertex* dfaStatep) {
// Find the NFA states this dfa came from,
// Record a checksum, so we can search for it later by the list of nfa nodes.
// The order of the nodes is not deterministic; the hash thus must not depend on order of edges
uint32_t hash = 0;
// Foreach NFA state (this DFA state was formed from)
if (debug()) nextStep();
for (V3GraphEdge* dfaEdgep = dfaStatep->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) {
if (nfaState(dfaEdgep->top())) {
DfaVertex* nfaStatep = static_cast<DfaVertex*>(dfaEdgep->top());
hash ^= hashVertex(nfaStatep);
if (debug()) {
if (nfaStatep->user()==m_step) v3fatalSrc("DFA state points to duplicate NFA state.");
nfaStatep->user(m_step);
}
}
}
return hash;
}
uint32_t hashDfaOrigins(const DfaStates& nfasWithInput) {
// Find the NFA states this dfa came from,
uint32_t hash = 0;
for (DfaStates::const_iterator nfaIt=nfasWithInput.begin(); nfaIt!=nfasWithInput.end(); ++nfaIt) {
DfaVertex* nfaStatep = *nfaIt;
hash ^= hashVertex(nfaStatep);
}
return hash;
}
bool compareDfaOrigins(const DfaStates& nfasWithInput, DfaVertex* dfa2p) {
// Return true if the NFA nodes both DFAs came from are the same list
// Assume there are no duplicates in either input list or NFAs under dfa2
nextStep();
// Mark all input vertexes
int num1s = 0;
for (DfaStates::const_iterator nfaIt=nfasWithInput.begin(); nfaIt!=nfasWithInput.end(); ++nfaIt) {
DfaVertex* nfaStatep = *nfaIt;
nfaStatep->user(m_step);
num1s++;
}
if (!num1s) v3fatalSrc("DFA node construction that contains no NFA states");
// Check comparison; must all be marked
// (Check all in dfa2p were in dfa1p)
int num2s = 0;
for (V3GraphEdge* dfaEdgep = dfa2p->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) {
if (nfaState(dfaEdgep->top())) {
if (dfaEdgep->top()->user() != m_step) return false;
num2s++;
}
}
// If we saw all of the nodes, then they have the same number of hits
// (Else something in dfa1p that wasn't in dfa2p)
if (num1s != num2s) return false;
// Match
return true;
}
void insertDfaOrigins(DfaVertex* dfaStatep) {
// Record the NFA states this dfa came from
uint32_t hash = hashDfaOrigins(dfaStatep);
m_hashMap.insert(make_pair(hash,dfaStatep));
}
DfaVertex* findDfaOrigins(const DfaStates& nfasWithInput) {
// Find another DFA state which comes from the identical set of NFA states
// The order of the nodes is not deterministic; the hash thus must not depend on order of edges
uint32_t hash = hashDfaOrigins(nfasWithInput);
pair <HashMap::iterator,HashMap::iterator> eqrange = m_hashMap.equal_range(hash);
for (HashMap::iterator it = eqrange.first; it != eqrange.second; ++it) {
DfaVertex* testp = it->second;
if (compareDfaOrigins(nfasWithInput, testp)) {
UINFO(9," DFA match for set: "<<testp<<endl);
return testp; // Identical
}
}
return NULL; // No match
}
void findNfasWithInput(DfaVertex* dfaStatep, DfaInput input,
DfaStates& nfasWithInput) {
// Return all NFA states, with the given input transition from
// the nfa states a given dfa state was constructed from.
nextStep();
nfasWithInput.clear(); // NFAs with given input
// Foreach NFA state (this DFA state was formed from)
for (V3GraphEdge* dfaEdgep = dfaStatep->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) {
if (nfaState(dfaEdgep->top())) {
DfaVertex* nfaStatep = static_cast<DfaVertex*>(dfaEdgep->top());
// Foreach input transition (on this nfaStatep)
for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) {
DfaEdge* cNfaEdgep = static_cast<DfaEdge*>(nfaEdgep);
if (cNfaEdgep->input() == input) {
DfaVertex* nextStatep = static_cast<DfaVertex*>(cNfaEdgep->top());
if (unseenNfaThisStep(nextStatep)) { // Not processed?
nfasWithInput.push_back(nextStatep);
nextStatep->user(m_step);
UINFO(9," Reachable "<<nextStatep<<endl);
}
}
}
}
}
// Expand the nfasWithInput list to include epsilon states reachable by those on nfasWithInput
for (DfaStates::iterator nfaIt=nfasWithInput.begin(); nfaIt!=nfasWithInput.end(); ++nfaIt) {
DfaVertex* nfaStatep = *nfaIt;
// Foreach epsilon-reachable (on this nfaStatep)
for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) {
DfaEdge* cNfaEdgep = static_cast<DfaEdge*>(nfaEdgep);
if (cNfaEdgep->epsilon()) {
DfaVertex* nextStatep = static_cast<DfaVertex*>(cNfaEdgep->top());
if (unseenNfaThisStep(nextStatep)) { // Not processed?
nfasWithInput.push_back(nextStatep);
nextStatep->user(m_step);
UINFO(9," Epsilon Reachable "<<nextStatep<<endl);
}
}
}
}
}
void main() {
UINFO(5,"Dfa to Nfa conversion...\n");
// Vertex::color() begin: 1 indicates vertex on DFA graph, 0=NFA graph
m_graphp->clearColors();
// Vertex::m_user begin: # indicates processed this m_step number
m_graphp->userClearVertices();
if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_nfa");
// Find NFA start
DfaVertex* nfaStartp = graphp()->findStart();
// Create new DFA State (start state) from the NFA states
DfaVertex* dfaStartp = newDfaVertex(nfaStartp);
DfaStates dfaUnprocps; // Unprocessed DFA nodes
dfaUnprocps.push_back(dfaStartp);
UINFO(5,"Starting state conversion...\n");
// Form DFA starting state from epsilon closure of NFA start
nextStep();
DfaStates workps; workps.push_back(nfaStartp);
while (!workps.empty()) { // While work
DfaVertex* nfaStatep = workps.back(); workps.pop_back();
//UINFO(9," Processing "<<nfaStatep<<endl);
nfaStatep->user(m_step); // Mark as processed
// Add a edge so we can find NFAs from a given DFA.
// The NFA will never see this edge, because we only look at TO edges.
new DfaEdge(graphp(), dfaStartp, nfaStatep, DfaEdge::NA());
// Find epsilon closure of this nfa node, and destinations to work list
for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) {
DfaEdge* cNfaEdgep = static_cast<DfaEdge*>(nfaEdgep);
DfaVertex* nfaStatep = static_cast<DfaVertex*>(nfaEdgep->top());
//UINFO(9," Consider "<<nfaEdgep->top()<<" EP "<<cNfaEdgep->epsilon()<<endl);
if (cNfaEdgep->epsilon()
&& unseenNfaThisStep(nfaStatep)) { // Not processed?
workps.push_back(nfaStatep);
}
}
}
if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_start");
insertDfaOrigins(dfaStartp);
int i=0;
UINFO(5,"Main state conversion...\n");
while (!dfaUnprocps.empty()) {
DfaVertex* dfaStatep = dfaUnprocps.back(); dfaUnprocps.pop_back();
UINFO(9," On dfaState "<<dfaStatep<<endl);
// From this dfaState, what corresponding nfaStates have what inputs?
set<DfaInput> inputs;
// Foreach NFA state (this DFA state was formed from)
for (V3GraphEdge* dfaEdgep = dfaStatep->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) {
if (nfaState(dfaEdgep->top())) {
DfaVertex* nfaStatep = static_cast<DfaVertex*>(dfaEdgep->top());
// Foreach input on this nfaStatep
for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) {
DfaEdge* cNfaEdgep = static_cast<DfaEdge*>(nfaEdgep);
if (!cNfaEdgep->epsilon()) {
if (inputs.find(cNfaEdgep->input()) == inputs.end()) {
inputs.insert(cNfaEdgep->input());
UINFO(9," Input to "<<dfaStatep<<" is "<<(void*)(cNfaEdgep->input())<<" via "<<nfaStatep<<endl);
}
}
}
}
}
// Foreach input state (NFA inputs of this DFA state)
for (set<DfaInput>::const_iterator inIt=inputs.begin(); inIt!=inputs.end(); ++inIt) {
DfaInput input = *inIt;
UINFO(9," ==="<<++i<<"=======================\n");
UINFO(9," On input "<<(void*)(input)<<endl);
// Find all states reachable for given input
DfaStates nfasWithInput;
findNfasWithInput(dfaStatep, input, nfasWithInput/*ref*/);
// nfasWithInput now maps to the DFA we want a transition to.
// Does a DFA already exist with this, and only this subset of NFA's?
DfaVertex* toDfaStatep = findDfaOrigins(nfasWithInput);
if (!toDfaStatep) {
// Doesn't exist, make new dfa state corresponding to this one,
toDfaStatep = newDfaVertex();
dfaUnprocps.push_back(toDfaStatep); // Add to process list
// Track what nfa's point to it.
for (DfaStates::const_iterator nfaIt=nfasWithInput.begin(); nfaIt!=nfasWithInput.end(); ++nfaIt) {
UINFO(9," NewContainsNfa "<<*nfaIt<<endl);
new DfaEdge (graphp(), toDfaStatep, *nfaIt, DfaEdge::NA());
if ((*nfaIt)->accepting()) toDfaStatep->accepting(true);
}
insertDfaOrigins(toDfaStatep);
}
// Add input transition
new DfaEdge (graphp(), dfaStatep, toDfaStatep, input);
if (debug()>=6) m_graphp->dumpDotFilePrefixed("step");
}
}
// Remove old NFA states
UINFO(5,"Removing NFA states...\n");
if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_withnfa");
for (V3GraphVertex* nextp,*vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=nextp) {
nextp = vertexp->verticesNextp();
if (nfaState(vertexp)) {
vertexp->unlinkDelete(m_graphp); vertexp=NULL;
}
}
UINFO(5,"Done.\n");
if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_done");
}
public:
GraphNfaToDfa(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: GraphAlg(graphp, edgeFuncp) {
m_step = 0;
main();
}
~GraphNfaToDfa() {}
};
void DfaGraph::nfaToDfa() {
GraphNfaToDfa (this, &V3GraphEdge::followAlwaysTrue);
}
//######################################################################
//######################################################################
// Algorithms - optimize a DFA structure
//
// Scan the DFA, cleaning up trailing states.
class DfaGraphReduce : GraphAlg {
private:
// METHODS
int debug() { return 0; }
DfaGraph* graphp() { return static_cast<DfaGraph*>(m_graphp); }
bool isDead(DfaVertex* vertexp) {
// A state is dead if not accepting, and goes nowhere
if (vertexp->accepting() || vertexp->start()) return false;
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (edgep->top() != vertexp) return false;
}
return true;
}
void optimize_accepting_out() {
// Delete outbound edges from accepting states
// (As once we've accepted, we no longer care about anything else.)
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
if (vvertexp->accepting()) {
for (V3GraphEdge* nextp,*edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
nextp = edgep->outNextp();
edgep->unlinkDelete(); edgep=NULL;
}
}
}
}
}
void optimize_orphans() {
// Remove states that don't come from start
// Presumably the previous optimization orphaned them.
// Vertex::m_user begin: 1 indicates on the work list, 2 processed
// (Otherwise we might have nodes on the list twice, and reference after deleting them.)
m_graphp->userClearVertices();
DfaVertex* startp = graphp()->findStart();
stack<V3GraphVertex*> workps; workps.push(startp);
// Mark all nodes connected to start
while (!workps.empty()) {
V3GraphVertex* vertexp = workps.top(); workps.pop();
vertexp->user(2); // Processed
// Add nodes from here to the work list
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
V3GraphVertex* tovertexp = edgep->top();
if (!tovertexp->user()) {
workps.push(tovertexp);
tovertexp->user(1);
}
}
}
// Delete all nodes not connected
for (V3GraphVertex* nextp,*vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=nextp) {
nextp = vertexp->verticesNextp();
if (!vertexp->user()) {
vertexp->unlinkDelete(m_graphp); vertexp=NULL;
}
}
}
void optimize_no_outbound() {
// Non-accepting states with no outbound transitions may be
// deleted. Then, any arcs feeding those states, and perhaps those
// states...
// Vertex::m_user begin: 1 indicates on the work list
// (Otherwise we might have nodes on the list twice, and reference after deleting them.)
m_graphp->userClearVertices();
// Find all dead vertexes
stack<DfaVertex*> workps;
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
workps.push(vvertexp);
vertexp->user(1);
} else {
// If ever remove this, need dyn cast below
v3fatalSrc("Non DfaVertex in dfa graph");
}
}
// While deadness... Delete and find new dead nodes.
while (!workps.empty()) {
DfaVertex* vertexp = workps.top(); workps.pop();
vertexp->user(0);
if (isDead(vertexp)) {
// Add nodes that go here to the work list
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
DfaVertex* fromvertexp = static_cast<DfaVertex*>(edgep->fromp());
if (fromvertexp != vertexp
&& !fromvertexp->user()) {
workps.push(static_cast<DfaVertex*>(fromvertexp));
fromvertexp->user(1);
}
}
// Transitions to this state removed by the unlink function
vertexp->unlinkDelete(m_graphp); vertexp=NULL;
}
}
}
public:
DfaGraphReduce(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
: GraphAlg(graphp, edgeFuncp) {
if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_in");
optimize_accepting_out();
if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_acc");
optimize_orphans();
if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_orph");
optimize_no_outbound();
if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_noout");
}
~DfaGraphReduce() {}
};
void DfaGraph::dfaReduce() {
DfaGraphReduce (this, &V3GraphEdge::followAlwaysTrue);
}

134
src/V3GraphDfa.h Normal file
View File

@ -0,0 +1,134 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Graph automata base class
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3GRAPHDFA_H_
#define _V3GRAPHDFA_H_ 1
#include "config.h"
#include <vector>
#include "V3Global.h"
#include "V3Graph.h"
class DfaGraph;
class DfaVertex;
class DfaEdge;
//=============================================================================
// NFA/DFA Graphs
/// The NFA graph consists of:
/// DfaVertex(START) The starting point
/// DfaVertex() Interior states
/// DfaVertex(ACCEPT) The completion point
///
/// Transitions include a list of all inputs (arbitrary user pointers),
/// or epsilon, represented as a empty list of inputs.
///
/// We're only looking for matches, so the only accepting states are
/// at the end of the transformations.
///
/// Common transforms:
///
/// "*": DfaVertex(START) --> [epsilon] -->DfaVertex(ACCEPT)
///
/// "L": ...->[ON_L]-->DfaVtx-->[epsilon]-->DfaVtx(ACCEPT)
///
/// "LR": ...->[ON_L]-->DfaVtx-->[epsilon]-->DfaVtx()
/// ->[ON_R]-->DfaVtx-->[epsilon]-->DfaVtx(ACCEPT)
///
/// "L|R": ...->DfaVtx-->[epsilon]-->DfaVtx-->[ON_L]-->DfaVtx()->[epsilon]-->DfaVtx(ACCEPT)
/// \->[epsilon]-->DfaVtx-->[ON_R]-->DfaVtx()->[epsilon]-/
///
/// "L*": ...->DfaVtx-->[epsilon]-->DfaVtx-->[ON_L]-->DfaVtx()->[epsilon]-->DfaVtx(ACCEPT)
/// | ^\----[epsilon]<-------/ |
/// \->[epsilon]-----------------------------------------/
class DfaGraph : public V3Graph {
// STATE
public:
DfaGraph() {}
virtual ~DfaGraph() {}
// METHODS
/// Find start node
DfaVertex* findStart();
/// Convert automata: NFA to DFA
void nfaToDfa();
/// Simplify a DFA automata
void dfaReduce();
};
//=============================================================================
// Vertex
class DfaVertex : public V3GraphVertex {
// Each DFA state is captured in this vertex.
// Start and accepting are members, rather then the more intuitive
// subclasses, as subclassing them would make it harder to inherit from here.
bool m_start; // Start state
bool m_accepting; // Accepting state?
public:
// CONSTRUCTORS
DfaVertex(DfaGraph* graphp, bool start=false, bool accepting=false)
: V3GraphVertex(graphp)
, m_start(start), m_accepting(accepting) {}
virtual DfaVertex* clone(DfaGraph* graphp) {
return new DfaVertex(graphp, start(), accepting()); }
virtual ~DfaVertex() {}
// ACCESSORS
virtual string dotShape() const { return (accepting()?"doublecircle":""); }
virtual string dotColor() const { return start()?"blue":(color()?"red":"black"); }
bool start() const { return m_start; }
void start(bool flag) { m_start=flag; }
bool accepting() const { return m_accepting; }
void accepting(bool flag) { m_accepting=flag; }
};
//============================================================================
/// Abstract type indicating a specific "input" to the NFA
typedef AstNUser* DfaInput;
//============================================================================
// Edge types
class DfaEdge : public V3GraphEdge {
DfaInput m_input;
public:
static DfaInput EPSILON() { return NULL; }
static DfaInput NA() { return AstNUser::fromInt(1); } // as in not-applicable
// CONSTRUCTORS
DfaEdge(DfaGraph* graphp, DfaVertex* fromp, DfaVertex* top, DfaInput input)
: V3GraphEdge(graphp, fromp, top, 1)
, m_input(input) {}
virtual ~DfaEdge() {}
// METHODS
virtual string dotColor() const { return na()?"yellow":epsilon()?"green":"black"; }
virtual string dotLabel() const { return na()?"":epsilon()?"e":cvtToStr((void*)(input())); }
virtual string dotStyle() const { return (na()||cutable())?"dashed":""; }
bool epsilon() const { return input()==EPSILON(); }
bool na() const { return input()==NA(); }
DfaInput input() const { return m_input; }
};
//============================================================================
#endif // Guard

323
src/V3GraphTest.cpp Normal file
View File

@ -0,0 +1,323 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Graph tests
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include "V3Global.h"
#include "V3Graph.h"
#include "V3GraphDfa.h"
//######################################################################
//######################################################################
// Test class
class V3GraphTest {
public:
// ***These tests only run with DEBUG ON***
//static int debug() { return 9; }
static int debug() { return 0; }
protected:
// MEMBERS
DfaGraph m_graph;
// METHODS - for children
virtual void runTest() = 0; // Run the test
virtual string name() = 0; // Name of the test
// Utilities
void dump() {
if (debug()>=9) {
m_graph.dumpDotFilePrefixed("v3graphtest_"+name());
}
}
public:
V3GraphTest() {}
virtual ~V3GraphTest() {}
void run() {
if (debug()) runTest();
}
};
//######################################################################
//######################################################################
// Vertices and nodes
class V3GraphTestVertex : public V3GraphVertex {
string m_name;
public:
V3GraphTestVertex(V3Graph* graphp, string name) : V3GraphVertex(graphp), m_name(name) {}
virtual ~V3GraphTestVertex() {}
// Accessors
virtual string name() const { return m_name; }
};
class V3GraphTestVarVertex : public V3GraphTestVertex {
public:
V3GraphTestVarVertex(V3Graph* graphp, string name) : V3GraphTestVertex(graphp, name) {}
virtual ~V3GraphTestVarVertex() {}
// Accessors
virtual string dotColor() const { return "blue"; }
};
//######################################################################
//######################################################################
// Test vertices and nodes
class V3GraphTestStrong : public V3GraphTest {
public:
virtual string name() { return "strong"; }
virtual void runTest() {
V3Graph* gp = &m_graph;
// Verify we break edges at a good point
// A simple alg would make 3 breaks, below only requires b->i to break
V3GraphTestVertex* i = new V3GraphTestVarVertex(gp,"*INPUTS*");
V3GraphTestVertex* a = new V3GraphTestVarVertex(gp,"a");
V3GraphTestVertex* b = new V3GraphTestVarVertex(gp,"b");
V3GraphTestVertex* g1 = new V3GraphTestVarVertex(gp,"g1");
V3GraphTestVertex* g2 = new V3GraphTestVarVertex(gp,"g2");
V3GraphTestVertex* g3 = new V3GraphTestVarVertex(gp,"g3");
V3GraphTestVertex* q = new V3GraphTestVarVertex(gp,"q");
new V3GraphEdge(gp, i, a, 2, true);
new V3GraphEdge(gp, a, b, 2, true);
new V3GraphEdge(gp, b, g1, 2, true);
new V3GraphEdge(gp, b, g2, 2, true);
new V3GraphEdge(gp, b, g3, 2, true);
new V3GraphEdge(gp, g1, a, 2, true);
new V3GraphEdge(gp, g3, g2, 2, true);
new V3GraphEdge(gp, g2, g3, 2, true);
new V3GraphEdge(gp, g1, q, 2, true);
new V3GraphEdge(gp, g2, q, 2, true);
new V3GraphEdge(gp, g3, q, 2, true);
gp->stronglyConnected(&V3GraphEdge::followAlwaysTrue);
dump();
UASSERT(i->color()!=a->color() && a->color() != g2->color() && g2->color() != q->color(), "Separate colors not assigned");
UASSERT(a->color()==b->color() && a->color()==g1->color(), "Strongly connected nodes not colored together");
UASSERT(g2->color()==g3->color(), "Strongly connected nodes not colored together");
}
};
class V3GraphTestAcyc : public V3GraphTest {
public:
virtual string name() { return "acyc"; }
virtual void runTest() {
V3Graph* gp = &m_graph;
// Verify we break edges at a good point
// A simple alg would make 3 breaks, below only requires b->i to break
V3GraphTestVertex* i = new V3GraphTestVarVertex(gp,"*INPUTS*");
V3GraphTestVertex* a = new V3GraphTestVarVertex(gp,"a");
V3GraphTestVertex* b = new V3GraphTestVarVertex(gp,"b");
V3GraphTestVertex* g1 = new V3GraphTestVarVertex(gp,"g1");
V3GraphTestVertex* g2 = new V3GraphTestVarVertex(gp,"g2");
V3GraphTestVertex* g3 = new V3GraphTestVarVertex(gp,"g3");
new V3GraphEdge(gp, i, a, 2, true);
new V3GraphEdge(gp, a, b, 2, true);
new V3GraphEdge(gp, b, g1, 2, true);
new V3GraphEdge(gp, b, g2, 2, true);
new V3GraphEdge(gp, b, g3, 2, true);
new V3GraphEdge(gp, g1, a, 2, true);
new V3GraphEdge(gp, g2, a, 2, true);
new V3GraphEdge(gp, g3, a, 2, true);
gp->acyclic(&V3GraphEdge::followAlwaysTrue);
gp->order();
dump();
}
};
class V3GraphTestVars : public V3GraphTest {
public:
virtual string name() { return "vars"; }
virtual void runTest() {
V3Graph* gp = &m_graph;
V3GraphTestVertex* clk = new V3GraphTestVarVertex(gp,"$clk");
V3GraphTestVertex* a = new V3GraphTestVarVertex(gp,"$a");
V3GraphTestVertex* a_dly = new V3GraphTestVarVertex(gp,"$a_dly");
V3GraphTestVertex* a_dlyblk= new V3GraphTestVarVertex(gp,"$a_dlyblk");
V3GraphTestVertex* b = new V3GraphTestVarVertex(gp,"$b");
V3GraphTestVertex* b_dly = new V3GraphTestVarVertex(gp,"$b_dly");
V3GraphTestVertex* b_dlyblk= new V3GraphTestVarVertex(gp,"$b_dlyblk");
V3GraphTestVertex* c = new V3GraphTestVarVertex(gp,"$c");
V3GraphTestVertex* i = new V3GraphTestVarVertex(gp,"$i");
V3GraphTestVertex* ap = new V3GraphTestVarVertex(gp,"$a_pre");
V3GraphTestVertex* bp = new V3GraphTestVarVertex(gp,"$b_pre");
V3GraphTestVertex* cp = new V3GraphTestVarVertex(gp,"$c_pre");
V3GraphTestVertex* n;
// Logical order between clk, and posedge blocks
// implemented by special CLK prod/cons?
// Required order between first x_DLY<=x_pre and final x<=x_DLY
// implemented by producer/consumer on a_dly signals
// Required order between first x_DLY<=x_pre and x_DLY<=setters
// implemented by fake dependency on _dlyblk
// Required order between x_DLY<=setters and final x<=x_DLY
// implemented by producer/consumer on a_dly signals
// Desired order between different _DLY blocks so we can elim temporaries
// implemented by cutable "pre" signal dependencies
n = new V3GraphTestVertex(gp,"*INPUTS*"); {
new V3GraphEdge(gp, n, clk, 2);
new V3GraphEdge(gp, n, i, 2);
}
V3GraphTestVertex* posedge = n = new V3GraphTestVertex(gp,"*posedge clk*"); {
new V3GraphEdge(gp, clk, n, 2);
}
// AssignPre's VarRefs on LHS: generate special BLK
// normal: VarRefs on LHS: generate normal
// underSBlock: VarRefs on RHS: consume 'pre' (required to save cutable tests)
n = new V3GraphTestVertex(gp,"a_dly<PRE=a"); {
new V3GraphEdge(gp, n, a_dlyblk, 2); // Block ordering
new V3GraphEdge(gp, n, a_dly, 2);
new V3GraphEdge(gp, ap, n, 2, true); // DESIRED delayed ordering (inp is required)
new V3GraphEdge(gp, posedge, n, 2);
}
n = new V3GraphTestVertex(gp,"b_dly<PRE=b"); {
new V3GraphEdge(gp, n, b_dlyblk, 2); // Block ordering
new V3GraphEdge(gp, n, b_dly, 2);
new V3GraphEdge(gp, bp, n, 2, true); // DESIRED delayed ordering
new V3GraphEdge(gp, posedge, n, 2);
}
// AssignDly's VarRefs on LHS: consume special BLK
// normal: VarRefs on LHS: generate normal
// underSBlock: VarRefs on RHS: generate 'pre' signals (cutable)
// SenItems: consume CLOCK dependency
n = new V3GraphTestVertex(gp,"a_dly<=b|c"); {
new V3GraphEdge(gp, a_dlyblk, n, 2); // Block ordering in
new V3GraphEdge(gp, n, a_dly, 2);
// Note we don't include ap as we're generating a_dly
new V3GraphEdge(gp, n, bp, 2); // DESIRED delayed usage
new V3GraphEdge(gp, n, cp, 2); // DESIRED delayed usage
new V3GraphEdge(gp, posedge, n, 2);
}
n = new V3GraphTestVertex(gp,"b_dly<=a"); {
new V3GraphEdge(gp, b_dlyblk, n, 2); // Block ordering in
new V3GraphEdge(gp, n, b_dly, 2);
new V3GraphEdge(gp, n, ap, 2); // DESIRED delayed usage
new V3GraphEdge(gp, posedge, n, 2);
}
// AssignPost's
// normal: VarRefs on LHS: generate normal
// underSBlock: VarRefs on RHS: consume normal
n = new V3GraphTestVertex(gp,"a=POST=a_dly"); {
new V3GraphEdge(gp, n, a, 3);
new V3GraphEdge(gp, a_dly, n, 3);
new V3GraphEdge(gp, posedge, n, 2);
}
n = new V3GraphTestVertex(gp,"b=POST=b_dly"); {
new V3GraphEdge(gp, n, b, 3);
new V3GraphEdge(gp, b_dly, n, 3);
new V3GraphEdge(gp, posedge, n, 2);
}
// COMBO
// Inbound edges are always uncutable, because we must put combo logic after sequential
// Outbound are cutable, as we may need to evaluate multiple times
{
V3GraphTestVertex* n = new V3GraphTestVertex(gp,"c=a|b|i");
new V3GraphEdge(gp, n, c, 1, true);
new V3GraphEdge(gp, a, n, 1, false);
new V3GraphEdge(gp, b, n, 1, false);
new V3GraphEdge(gp, i, n, 1, false);
}
gp->acyclic(&V3GraphEdge::followAlwaysTrue);
gp->order();
dump();
}
};
//======================================================================
class DfaTestVertex : public DfaVertex {
string m_name;
public:
DfaTestVertex(DfaGraph* graphp, string name) : DfaVertex(graphp), m_name(name) {}
virtual ~DfaTestVertex() {}
// Accessors
virtual string name() const { return m_name; }
};
class V3GraphTestDfa : public V3GraphTest {
public:
virtual string name() { return "dfa"; }
virtual void runTest() {
DfaGraph* gp = &m_graph;
// NFA Pattern for ( (LR) | (L*R)) Z
DfaTestVertex* st = new DfaTestVertex(gp,"*START*"); st->start(true);
DfaTestVertex* sl = new DfaTestVertex(gp,"sL");
DfaTestVertex* srs = new DfaTestVertex(gp,"sR*");
DfaTestVertex* sls = new DfaTestVertex(gp,"sL*");
DfaTestVertex* sr = new DfaTestVertex(gp,"sR");
DfaTestVertex* sz = new DfaTestVertex(gp,"sZ");
DfaTestVertex* sac = new DfaTestVertex(gp,"*ACCEPT*"); sac->accepting(true);
AstNUser* L = AstNUser::fromInt(0xaa);
AstNUser* R = AstNUser::fromInt(0xbb);
AstNUser* Z = AstNUser::fromInt(0xcc);
new DfaEdge(gp, st, sl, DfaEdge::EPSILON());
new DfaEdge(gp, sl, srs, L);
new DfaEdge(gp, srs, srs, R);
new DfaEdge(gp, srs, sz, Z);
new DfaEdge(gp, sz, sac, DfaEdge::EPSILON());
new DfaEdge(gp, st, sls, DfaEdge::EPSILON());
new DfaEdge(gp, sls, sls, L);
new DfaEdge(gp, sls, sr, R);
new DfaEdge(gp, sr, sz, Z);
new DfaEdge(gp, sz, sac, DfaEdge::EPSILON());
dump();
gp->nfaToDfa();
dump();
gp->dfaReduce();
}
};
//======================================================================
void V3Graph::test() {
// Execute all of the tests
UINFO(2,__FUNCTION__<<": "<<endl);
{ V3GraphTestStrong test; test.run(); }
{ V3GraphTestAcyc test; test.run(); }
{ V3GraphTestVars test; test.run(); }
{ V3GraphTestDfa test; test.run(); }
if (V3GraphTest::debug()) v3fatalSrc("Exiting due to graph testing enabled");
}

136
src/V3Hashed.cpp Normal file
View File

@ -0,0 +1,136 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Hashed common code into functions
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Hashed's Transformations:
//
// Hash each node depth first
// Hash includes varp name and operator type, and constants
// Form lookup table based on hash of each statement w/ nodep and next nodep
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include "V3Global.h"
#include "V3Hashed.h"
#include "V3Ast.h"
//######################################################################
// Hashed state, as a visitor of each AstNode
class HashedVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist:
// AstNodeStmt::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
// STATE
V3Hash m_lowerHash; // Hash of the statement we're building
//int debug() { return 9; }
// METHODS
void hashNodeIterate(AstNode* nodep) {
if (!nodep->user4()) {
if (nodep->backp()->castCFunc()
&& !(nodep->castNodeStmt() || nodep->castCFunc())) {
nodep->v3fatalSrc("Node "<<nodep->typeName()<<" in statement position but not marked stmt (node under function)");
}
V3Hash oldHash = m_lowerHash;
{
m_lowerHash = nodep->sameHash();
if (m_lowerHash.isIllegal()) {
nodep->v3fatalSrc("sameHash function undefined (returns 0) for node under CFunc.");
}
m_lowerHash = V3Hash(m_lowerHash, V3Hash(nodep->type()<<6 | nodep->width()));
// Now update m_lowerHash for our children's (and next children) contributions
nodep->iterateChildren(*this);
// Store the hash value
nodep->user4(m_lowerHash.fullValue());
//UINFO(9, " hashnode "<<m_lowerHash<<" "<<nodep<<endl);
}
m_lowerHash = oldHash;
}
// Update what will become the above node's hash
m_lowerHash += V3Hash(nodep->user4p());
}
//--------------------
// Default: Just iterate
virtual void visit(AstVar*, AstNUser*) {}
virtual void visit(AstNode* nodep, AstNUser*) {
hashNodeIterate(nodep);
}
public:
// CONSTUCTORS
HashedVisitor(AstNode* nodep) {
hashNodeIterate(nodep);
//UINFO(9," stmthash "<<hex<<nodep->user4()<<" "<<nodep<<endl);
}
virtual ~HashedVisitor() {}
};
//######################################################################
// Hashed class functions
V3Hashed::V3Hashed() {
AstNode::user4ClearTree(); // userp() used on entire tree
}
void V3Hashed::hashAndInsert(AstNode* nodep) {
UINFO(8," hashI "<<nodep<<endl);
if (!nodep->user4p()) {
HashedVisitor visitor (nodep);
}
m_hashMmap.insert(make_pair(V3Hash(nodep->user4p()), nodep));
}
bool V3Hashed::sameNodes(AstNode* node1p, AstNode* node2p) {
if (!node1p->user4p()) node1p->v3fatalSrc("Called isIdentical on non-hashed nodes");
if (!node2p->user4p()) node2p->v3fatalSrc("Called isIdentical on non-hashed nodes");
return (node1p->user4p() == node2p->user4p() // Same hash
&& node1p->sameTree(node2p));
}
void V3Hashed::erase(iterator it) {
AstNode* nodep = iteratorNodep(it);
UINFO(8," erase "<<nodep<<endl);
if (!nodep->user4p()) nodep->v3fatalSrc("Called removeNode on non-hashed node");
m_hashMmap.erase(it);
nodep->user4p(NULL); // So we don't allow removeNode again
}
V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep) {
UINFO(8," findD "<<nodep<<endl);
if (!nodep->user4p()) nodep->v3fatalSrc("Called findDuplicate on non-hashed node");
pair <HashMmap::iterator,HashMmap::iterator> eqrange = mmap().equal_range(V3Hash(nodep->user4p()));
for (HashMmap::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
AstNode* node2p = eqit->second;
if (nodep != node2p && sameNodes(nodep, node2p)) {
return eqit;
}
}
return end();
}

61
src/V3Hashed.h Normal file
View File

@ -0,0 +1,61 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Hash AST trees to find duplicates
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2005-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3HASHED_H_
#define _V3HASHED_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
#include <map>
//============================================================================
class V3Hashed {
// NODE STATE
// AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal)
// TYPES
typedef multimap<V3Hash,AstNode*> HashMmap;
public:
typedef HashMmap::iterator iterator;
private:
// MEMBERS
HashMmap m_hashMmap; // hashvalue -> nodes with that hash
int debug() { return 0; }
public:
// CONSTRUCTORS
V3Hashed();
~V3Hashed() {}
// ACCESSORS
HashMmap& mmap() { return m_hashMmap; } // Return map for iteration
HashMmap::iterator begin() { return m_hashMmap.begin(); }
HashMmap::iterator end() { return m_hashMmap.end(); }
// METHODS
void clear() { m_hashMmap.clear(); }
void hashAndInsert(AstNode* nodep); // Hash the node, and insert into map
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
AstNode* iteratorNodep(iterator it) { return it->second; }
};
#endif // Guard

407
src/V3Inline.cpp Normal file
View File

@ -0,0 +1,407 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Add temporaries, such as for inline nodes
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Inline's Transformations:
//
// Each module:
// Look for CELL... PRAGMA INLINE_MODULE
// Replicate the cell's module
// Convert pins to wires that make assignments
// Rename vars to include cell name
// Insert cell's module statements into the upper module
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include <vector>
#include "V3Global.h"
#include "V3Inline.h"
#include "V3Inst.h"
#include "V3Stats.h"
#include "V3Ast.h"
//######################################################################
// 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 InlineVisitor : public AstNVisitor {
private:
// NODE STATE
// Cleared entire netlist
// AstModule::userp() // bool. True to inline this module (from InlineMarkVisitor)
// Cleared each cell
// AstVar::user2p() // AstVarRef*/AstConst* Points to signal this is a direct connect to
// STATE
AstModule* m_modp; // Current module
AstCell* m_cellp; // Cell being cloned
V3Double0 m_statCells; // Statistic tracking
//int debug() { return 9; }
// VISITORS
virtual void visit(AstNetlist* nodep, AstNUser*) {
// Iterate modules backwards, in bottom-up order. Required!
nodep->iterateChildrenBackwards(*this);
}
virtual void visit(AstModule* nodep, AstNUser*) {
if (m_cellp) {
} else {
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()->userp()) { // 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++;
if (debug()>=9) { nodep->dumpTree(cout,"inlcell:"); }
//if (debug()>=9) { nodep->modp()->dumpTree(cout,"oldmod:"); }
AstModule* newmodp = nodep->modp()->cloneTree(false)->castModule();
if (debug()>=9) { newmodp->dumpTree(cout,"newmod:"); }
// Clear var markings
AstNode::user2ClearTree();
// Create data for dotted variable resolution
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
nodep->name(), nodep->modp()->origName());
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
// Create assignments to the pins
AstNode* assignlistsp = NULL;
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
UINFO(6," Pin change from "<<pinp->modVarp()<<endl);
// First, simplify it
V3Inst::pinReconnectSimple(pinp, nodep, m_modp);
// Make new signal; even though we'll optimize the interconnect, we
// need an alias to trace correctly. If tracing is disabled, we'll
// delete it in later optimizations.
AstVar* pinOldVarp = pinp->modVarp();
AstVar* pinNewVarp = pinOldVarp->clonep()->castVar();
AstNode* connectRefp = pinp->exprp();
if (!connectRefp->castConst() && !connectRefp->castVarRef()) {
pinp->v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up\n");
}
// Propagate any attributes across the interconnect
pinNewVarp->propagateAttrFrom(pinOldVarp);
if (connectRefp->castVarRef()) {
connectRefp->castVarRef()->varp()->propagateAttrFrom(pinOldVarp);
}
// One to one interconnect won't make a temporary variable.
// This prevents creating a lot of extra wires for clock signals.
// It will become a tracing alias.
UINFO(6,"One-to-one "<<connectRefp<<endl);
UINFO(6," -to "<<pinNewVarp<<endl);
pinNewVarp->user2p(connectRefp);
}
// Cleanup var names, etc, to not conflict
m_cellp = nodep;
newmodp->iterateAndNext(*this);
if (assignlistsp) assignlistsp->iterateAndNext(*this); // And cleanup any varrefs under assigns we created...
m_cellp = NULL;
// Move statements to top module
if (debug()>=9) { newmodp->dumpTree(cout,"fixmod:"); }
AstNode* stmtsp = newmodp->stmtsp();
if (stmtsp) stmtsp->unlinkFrBackWithNext();
if (assignlistsp) m_modp->addStmtp(assignlistsp);
if (stmtsp) m_modp->addStmtp(stmtsp);
// Remove the cell
newmodp->deleteTree(); newmodp=NULL; // Clear any leftover ports, etc
nodep->unlinkFrBack();
nodep = NULL;
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();
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 {
m_modp->addStmtp(new AstAssignAlias(nodep->fileline(),
new AstVarRef(nodep->fileline(), nodep, true),
new AstVarRef(nodep->fileline(), exprvarrefp->varp(), false)));
}
}
// 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->initp()->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(AstVarRef* nodep, AstNUser*) {
if (m_cellp) {
if (nodep->varp()->user2p() // It's being converted to a alias.
&& !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases
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);
}
virtual void visit(AstDisplay* nodep, AstNUser*) {
// If there's a %m in the display text, we add a special node that will contain the name()
if (m_cellp
&& nodep->name().find("%m") != string::npos) {
// 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)"."+m_cellp->prettyName()));
if (afterp) nodep->scopeAttrp(afterp);
}
nodep->iterateChildren(*this);
}
virtual void visit(AstCoverDecl* nodep, AstNUser*) {
// Fix path in coverage statements
if (m_cellp) {
nodep->hier(nodep->hier()+"."+m_cellp->prettyName());
}
nodep->iterateChildren(*this);
}
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
InlineVisitor(AstNode* nodep) {
m_cellp = NULL;
m_modp = NULL;
nodep->accept(*this);
}
virtual ~InlineVisitor() {
V3Stats::addStat("Optimizations, Inlined cells", m_statCells);
}
};
//######################################################################
// Inline state, as a visitor of each AstNode
class InlineMarkVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist
// AstModule::user() // OUTPUT: bool. User request to inline this module
// AstModule::user2() // bool. Allowed to automatically inline module
// AstModule::user3() // int. Number of cells referencing this module
// STATE
AstModule* 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(AstModule* nodep, AstNUser*) {
m_stmtCnt = 0;
m_modp = nodep;
m_modp->user2(true);
if (m_modp->modPublic()) cantInline("modPublic");
//
nodep->iterateChildren(*this);
//
bool userinline = nodep->user();
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()));
UINFO(4, " Inline="<<doit<<" Possible="<<allowed<<" Usr="<<userinline<<" Refs="<<refs<<" Stmts="<<m_stmtCnt
<<" "<<nodep<<endl);
if (doit) {
UINFO(4," AutoInline "<<nodep<<endl);
nodep->user(true);
}
m_modp = NULL;
}
virtual void visit(AstCell* nodep, AstNUser*) {
nodep->modp()->user3( nodep->modp()->user3() + 1);
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->user(1);
}
nodep->unlinkFrBack(); 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(); 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
nodep->taskp(NULL);
}
// 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::userClearTree();
AstNode::user2ClearTree();
AstNode::user3ClearTree();
nodep->accept(*this);
}
virtual ~InlineMarkVisitor() {}
};
//######################################################################
// Inline class functions
void V3Inline::inlineAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
InlineMarkVisitor mvisitor (nodep);
InlineVisitor visitor (nodep);
// Remove all modules that were inlined
// V3Dead will also clean them up, but if we have debug on, it's a good
// idea to avoid dumping the hugely exploded tree.
AstModule* nextmodp;
for (AstModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
nextmodp = modp->nextp()->castModule();
if (modp->userp()) { // Was inlined
modp->unlinkFrBack()->deleteTree(); modp=NULL;
}
}
}

35
src/V3Inline.h Normal file
View File

@ -0,0 +1,35 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Inlining of modules
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3INLINE_H_
#define _V3INLINE_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Inline {
public:
static void inlineAll(AstNetlist* nodep);
};
#endif // Guard

281
src/V3Inst.cpp Normal file
View File

@ -0,0 +1,281 @@
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Add temporaries, such as for inst nodes
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// V3Inst's Transformations:
//
// Each module:
// Pins:
// Create a wire assign to interconnect to submodule
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <algorithm>
#include "V3Global.h"
#include "V3Inst.h"
#include "V3Ast.h"
#include "V3Changed.h"
//######################################################################
// Inst state, as a visitor of each AstNode
class InstVisitor : public AstNVisitor {
private:
// NODE STATE
// Cleared each Cell:
// AstVar::userp() -> AstNode*. Expression connected to given pin
// AstVarRef::userp() -> bool. True if created senitem for parent's connected signal
// AstPin::userp() -> bool. True if created assignment already
// STATE
AstModule* m_modp; // Current module
AstCell* m_cellp; // Current cell
//int debug() { return 8; }
//int m_debug; int debug() { return m_debug; }
// VISITORS
virtual void visit(AstModule* nodep, AstNUser*) {
UINFO(4," MOD "<<nodep<<endl);
//if (nodep->name() == "t_chg") m_debug = 9; else m_debug=0;
m_modp = nodep;
nodep->iterateChildren(*this);
}
virtual void visit(AstCell* nodep, AstNUser*) {
UINFO(4," CELL "<<nodep<<endl);
m_cellp = nodep;
//VV***** We reset userp() on each cell!!!
AstNode::userClearTree();
// Collect pin expressions, so submod->varp->userp() points to expression it connects to
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
pinp->modVarp()->userp(pinp->exprp());
}
nodep->iterateChildren(*this);
m_cellp = NULL;
}
virtual void visit(AstPin* nodep, AstNUser*) {
// PIN(p,expr) -> ASSIGNW(VARXREF(p),expr) (if sub's input)
// or ASSIGNW(expr,VARXREF(p)) (if sub's output)
UINFO(4," PIN "<<nodep<<endl);
if (debug()>=9) nodep->dumpTree(cout," Pin_oldb: ");
if (nodep->modVarp()->isOutput() && nodep->exprp()->castConst())
nodep->v3error("Output pin is assigned to a constant, electrical short");
// Use userp on the PIN to indicate we created an assign for this pin
if (!nodep->user()) {
nodep->user(1);
// Simplify it
V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp);
// Make a ASSIGNW (expr, pin)
AstNode* exprp = nodep->exprp()->cloneTree(false);
if (nodep->width() != nodep->modVarp()->width())
nodep->v3fatalSrc("Width mismatch, should have been handled in pinReconnectSimple\n");
if (nodep->modVarp()->isOutput()) {
AstNode* rhsp = new AstVarXRef (exprp->fileline(), nodep->modVarp(), m_cellp->name(), false);
rhsp->widthSignedFrom(nodep);
AstAssignW* assp = new AstAssignW (exprp->fileline(), exprp, rhsp);
m_modp->addStmtp(assp);
} else if (nodep->modVarp()->isInput()) {
// Don't bother moving constants now,
// we'll be pushing the const down to the cell soon enough.
AstNode* assp = new AstAssignW
(exprp->fileline(),
new AstVarXRef(exprp->fileline(), nodep->modVarp(), m_cellp->name(), true),
exprp);
m_modp->addStmtp(assp);
if (debug()>=9) assp->dumpTree(cout," _new: ");
} else {
nodep->v3error("Assigned pin is neither input nor output");
}
}
// We're done with the pin
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
// Save some time
virtual void visit(AstNodeAssign*, AstNUser*) {}
virtual void visit(AstAlways*, AstNUser*) {}
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
InstVisitor(AstNode* nodep) {
m_modp=NULL;
m_cellp=NULL;
//
nodep->accept(*this);
}
virtual ~InstVisitor() {}
};
//######################################################################
class InstDeVisitor : public AstNVisitor {
// Find all cells with arrays, and convert to non-arrayed
private:
// STATE
AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations
int m_instNum; // Current instantiation number
int m_instLsb; // Current instantiation number
//int debug() { return 9; }
// VISITORS
virtual void visit(AstCell* nodep, AstNUser*) {
if (nodep->rangep()) {
m_cellRangep = nodep->rangep();
UINFO(4," CELL "<<nodep<<endl);
// Make all of the required clones
m_instLsb = m_cellRangep->lsbConst();
for (m_instNum = m_instLsb; m_instNum<=m_cellRangep->msbConst(); m_instNum++) {
AstCell* newp = nodep->cloneTree(false)->castCell();
nodep->addNextHere(newp);
// Remove ranging and fix name
newp->rangep()->unlinkFrBack()->deleteTree();
newp->name(newp->name()+"__"+cvtToStr(m_instNum));
// Fixup pins
newp->pinsp()->iterateAndNext(*this);
if (debug()==9) { newp->dumpTree(cout,"newcell: "); cout<<endl; }
}
// Done. Delete original
m_cellRangep=NULL;
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
}
}
virtual void visit(AstPin* nodep, AstNUser*) {
// Any non-direct pins need reconnection with a part-select
if (m_cellRangep) {
UINFO(4," PIN "<<nodep<<endl);
int pinwidth = nodep->modVarp()->width();
int expwidth = nodep->exprp()->width();
if (expwidth == pinwidth) {
// NOP: Arrayed instants: widths match so connect to each instance
} else if (expwidth == pinwidth*m_cellRangep->width()) {
// Arrayed instants: one bit for each of the instants (each assign is 1 pinwidth wide)
AstNode* exprp = nodep->exprp()->unlinkFrBack();
bool inputPin = nodep->modVarp()->isInput();
if (!inputPin && !exprp->castVarRef()) {
nodep->v3error("Unsupported: Per-bit array instantiations with output connections to non-wires.");
}
exprp = new AstSel (exprp->fileline(), exprp,
pinwidth*(m_instNum-m_instLsb),
pinwidth);
nodep->exprp(exprp);
} else {
nodep->v3fatalSrc("Width mismatch; V3Width should have errored out.");
}
}
}
// Save some time
virtual void visit(AstNodeStmt*, AstNUser*) {}
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
InstDeVisitor(AstNode* nodep) {
m_cellRangep=NULL;
m_instNum=0;
m_instLsb=0;
//
nodep->accept(*this);
}
virtual ~InstDeVisitor() {}
};
//######################################################################
// Inst class functions
void V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstModule* modp) {
// If a pin connection is "simple" leave it as-is
// Else create a intermediate wire to perform the interconnect
AstVar* pinVarp = pinp->modVarp();
AstVarRef* connectRefp = pinp->exprp()->castVarRef();
if (connectRefp
&& connectRefp->width() == pinVarp->width()
&& connectRefp->varp()->lsb() == pinVarp->lsb()
&& !connectRefp->varp()->isSc() // Need the signal as a 'shell' to convert types
&& pinp->width() == pinVarp->width()
&& 1) {
// Done. One to one interconnect won't need a temporary variable.
} else if (pinp->exprp()->castConst()) {
// Done. Constant.
} else {
// Make a new temp wire
//if (1||debug()>=9) { pinp->dumpTree(cout,"in_pin:"); }
AstAssignW* assignp;
AstNode* pinexprp = pinp->exprp()->unlinkFrBack();
string newvarname = "__Vcellinp__"+cellp->name()+"__"+pinp->name();
AstVar* newvarp = new AstVar (pinVarp->fileline(), AstVarType::MODULETEMP, newvarname, pinVarp);
modp->addStmtp(newvarp);
if (pinVarp->isOutput()) {
// See also V3Inst
AstNode* rhsp = new AstVarRef(pinp->fileline(), newvarp, false);
if (pinp->width() > rhsp->width()) {
if (rhsp->isSigned()) {
rhsp = new AstExtendS(pinp->fileline(), rhsp);
} else {
rhsp = new AstExtend (pinp->fileline(), rhsp);
}
} else if (pinp->width() < rhsp->width()) {
rhsp = new AstSel (pinp->fileline(), rhsp, 0, pinp->widthMin());
}
rhsp->widthSignedFrom(pinp);
assignp = new AstAssignW (pinp->fileline(), pinexprp, rhsp);
pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, true));
} else {
// V3 width should have range/extended to make the widths correct
if (pinexprp->width() != pinVarp->width()) pinp->v3fatalSrc("Input pin width mismatch");
assignp = new AstAssignW (pinp->fileline(),
new AstVarRef(pinp->fileline(), newvarp, true),
pinexprp);
pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, false));
}
pinp->widthSignedFrom(pinp->exprp());
modp->addStmtp(assignp);
//if (1||debug()) { pinp->dumpTree(cout," out:"); }
//if (1||debug()) { assignp->dumpTree(cout," aout:"); }
}
}
//######################################################################
// Inst class visitor
void V3Inst::instAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
InstVisitor visitor (nodep);
}
void V3Inst::dearrayAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
InstDeVisitor visitor (nodep);
}

37
src/V3Inst.h Normal file
View File

@ -0,0 +1,37 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Break always into sensitivity inst domains
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3INST_H_
#define _V3INST_H_ 1
#include "config.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Inst {
public:
static void instAll(AstNetlist* nodep);
static void dearrayAll(AstNetlist* nodep);
static void pinReconnectSimple(AstPin* nodep, AstCell* cellp, AstModule* modp);
};
#endif // Guard

125
src/V3LanguageWords.h Normal file
View File

@ -0,0 +1,125 @@
// $Id$ //-*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Language rules
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2005-2006 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#ifndef _V3LANGUAGEWORDS_H_
#define _V3LANGUAGEWORDS_H_ 1
#include "config.h"
#include <map>
//============================================================================
class V3LanguageWords {
// List of common reserved keywords
private:
map<string,string> m_kwdMap; // List of keywords, and what language applies
void addKwd(const string& kwd, const string& why) {
m_kwdMap.insert(make_pair(kwd,why));
}
public:
string isKeyword(const string& kwd) {
map<string,string>::iterator iter = m_kwdMap.find(kwd);
if (iter == m_kwdMap.end()) return "";
return iter->second;
}
public:
V3LanguageWords() {
// C++ keywords
addKwd("asm", "C++ reserved word");
addKwd("auto", "C++ reserved word");
addKwd("catch", "C++ reserved word");
addKwd("cdecl", "C++ reserved word");
addKwd("char", "C++ reserved word");
addKwd("const_cast", "C++ reserved word");
addKwd("delete", "C++ reserved word");
addKwd("double", "C++ reserved word");
addKwd("dynamic_cast", "C++ reserved word");
addKwd("explicit", "C++ reserved word");
addKwd("far", "C++ reserved word");
addKwd("float", "C++ reserved word");
addKwd("friend", "C++ reserved word");
addKwd("goto", "C++ reserved word");
addKwd("huge", "C++ reserved word");
addKwd("inline", "C++ reserved word");
addKwd("interrupt", "C++ reserved word");
addKwd("long", "C++ reserved word");
addKwd("mutable", "C++ reserved word");
addKwd("near", "C++ reserved word");
addKwd("operator", "C++ reserved word");
addKwd("pascal", "C++ reserved word");
addKwd("private", "C++ reserved word");
addKwd("public", "C++ reserved word");
addKwd("register", "C++ reserved word");
addKwd("reinterpret_cast ", "C++ reserved word");
addKwd("restrict", "C++ reserved word");
addKwd("short", "C++ reserved word");
addKwd("sizeof", "C++ reserved word");
addKwd("static_cast", "C++ reserved word");
addKwd("switch", "C++ reserved word");
addKwd("template", "C++ reserved word");
addKwd("throw", "C++ reserved word");
addKwd("try", "C++ reserved word");
addKwd("typeid", "C++ reserved word");
addKwd("typename", "C++ reserved word");
addKwd("unsigned", "C++ reserved word");
addKwd("using", "C++ reserved word");
addKwd("volatile", "C++ reserved word");
// C++
addKwd("NULL", "C++ common word");
addKwd("abort", "C++ common word");
addKwd("bit_vector", "C++ common word");
addKwd("bool", "C++ common word");
addKwd("complex", "C++ common word");
addKwd("const_iterator", "C++ common word");
addKwd("const_reference ", "C++ common word");
addKwd("deque", "C++ common word");
addKwd("false", "C++ common word");
addKwd("iterator", "C++ common word");
addKwd("list", "C++ common word");
addKwd("map", "C++ common word");
addKwd("multimap", "C++ common word");
addKwd("multiset", "C++ common word");
addKwd("queue", "C++ common word");
addKwd("reference", "C++ common word");
addKwd("set", "C++ common word");
addKwd("stack", "C++ common word");
addKwd("true", "C++ common word");
addKwd("type_info", "C++ common word");
addKwd("uint16_t", "C++ common word");
addKwd("uint32_t", "C++ common word");
addKwd("uint8_t", "C++ common word");
addKwd("vector", "C++ common word");
// SystemC
addKwd("sc_clock", "SystemC common word");
addKwd("sc_in", "SystemC common word");
addKwd("sc_inout", "SystemC common word");
addKwd("sc_out", "SystemC common word");
addKwd("sc_signal", "SystemC common word");
addKwd("sensitive_neg", "SystemC common word");
addKwd("sensitive_pos", "SystemC common word");
}
};
#endif // Guard

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