Version bump
git-svn-id: file://localhost/svn/verilator/trunk/verilator@753 77ca24e4-aefa-0310-84f0-b9a241c72d87
This commit is contained in:
commit
ce10dbd11c
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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:
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
@ -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 (===)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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,
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
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:
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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([])
|
||||
|
|
@ -0,0 +1 @@
|
|||
verilated.mk
|
||||
|
|
@ -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) {
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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*/
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
"<>" => \¶meter,
|
||||
"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:
|
||||
|
|
@ -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,
|
||||
"<>" => \¶meter,
|
||||
)) {
|
||||
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:
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
*.old
|
||||
config.h
|
||||
Makefile
|
||||
Makefile_obj
|
||||
.objcache*
|
||||
obj_*
|
||||
config_rev.h
|
||||
|
|
@ -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*
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
Loading…
Reference in New Issue