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