commit ce10dbd11c9e43c6a041d26520f02ccd69218520 Author: Wilson Snyder Date: Sat Aug 26 11:35:28 2006 +0000 Version bump git-svn-id: file://localhost/svn/verilator/trunk/verilator@753 77ca24e4-aefa-0310-84f0-b9a241c72d87 diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 000000000..154a707dd --- /dev/null +++ b/.cvsignore @@ -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 diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..916d1f0f2 --- /dev/null +++ b/COPYING @@ -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. + + + Copyright (C) 19yy + + 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. + + , 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. diff --git a/Changes b/Changes new file mode 100644 index 000000000..cc872b130 --- /dev/null +++ b/Changes @@ -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>>, $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: diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 000000000..08604659e --- /dev/null +++ b/Makefile.in @@ -0,0 +1,353 @@ +# $Id$ +#***************************************************************************** +# DESCRIPTION: Verilator top level: Makefile pre-configure version +# +# This file is part of Verilator. +# +# Author: Wilson Snyder or +# +# 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 diff --git a/README b/README new file mode 100644 index 000000000..3514fedeb --- /dev/null +++ b/README @@ -0,0 +1,167 @@ +1 Verilator +*********** + +This is the Verilator Package. + +1.1 Copyright +============= + +This package is Copyright 2003-2006 by Wilson Snyder +. + + 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. + diff --git a/TODO b/TODO new file mode 100644 index 000000000..2dd701eee --- /dev/null +++ b/TODO @@ -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 //<< 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 + b) tri + 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 (===) + diff --git a/bin/verilator b/bin/verilator new file mode 100755 index 000000000..a2a929108 --- /dev/null +++ b/bin/verilator @@ -0,0 +1,1622 @@ +: # -*-Mode: perl;-*- use perl, wherever it is +eval 'exec perl -wS $0 ${1+"$@"}' + if 0; +# $Id$ +###################################################################### +# +# 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 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; +BEGIN { + if (my $Project=($ENV{DIRPROJECT}||$ENV{PROJECT})) { + # Magic to allow author testing of perl packages in local directory + require "$Project/hw/utils/perltools/boot.pl"; + } +} + +use File::Path; +use File::Basename; +use Getopt::Long; +use FindBin qw($RealBin $RealScript); +use IO::File; +use Pod::Usage; +use Config; +use Cwd qw(abs_path getcwd); + +use strict; +use vars qw ($Debug %Vars $Opt $Opt_Make_Dir $Opt_Sp @Opt_Verilator_Sw + $Opt_Trace + %Modules + ); + +####################################################################### +# Global constants -- Configuration info + +# Where to find real executables +# We could find it by using $RealBin, but we require this path +# so that when the user runs make, they will have it and not get strange error. +$ENV{VERILATOR_ROOT} or die "%Error: verilator: VERILATOR_ROOT needs to be in environment\n"; +print "export VERILATOR_ROOT=$ENV{VERILATOR_ROOT}\n" if $Debug; + +# Read by verilator for populating makefile +if (!defined $ENV{SYSTEMC_ARCH}) { + $ENV{SYSTEMC_ARCH} ||= (($Config{osname} =~ /solaris/i && "gccsparcOS5") + || ($Config{osname} =~ /cygwin/i && "cygwin") + || "linux"); + print "export SYSTEMC_ARCH=$ENV{SYSTEMC_ARCH}\n" if $Debug; +} + +####################################################################### +####################################################################### +# main + +autoflush STDOUT 1; +autoflush STDERR 1; + +$Debug = 0; +my $opt_gdb; +$Opt_Sp = undef; + +# No arguments can't do anything useful. Give help +if ($#ARGV < 0) { + pod2usage(-exitstatus=>2, -verbose=>0); +} + +# Insert debugging options up front +push @ARGV, (split ' ',$ENV{VERILATOR_TEST_FLAGS}||""); + +# We sneak a look at the flags so we can do some pre-environment checks +# All flags will hit verilator... +@Opt_Verilator_Sw = @ARGV; + +Getopt::Long::config ("no_auto_abbrev","pass_through"); +if (! GetOptions ( + # Major operating modes + "help" => \&usage, + "debug:s" => \&debug, + "version!" => \&version, + # Switches + "gdb=s" => \$opt_gdb, # Undocumented debugging + "trace!" => \$Opt_Trace, + "sp!" => sub {$Opt_Sp = 'sp';}, + "sc!" => sub {$Opt_Sp = 'sc';}, + "cc!" => sub {$Opt_Sp = 0;}, + #"ignc!" => ..., # Undocumented debugging, disable $c but don't complain + # Additional parameters + "<>" => sub {}, # Ignored + )) { + pod2usage(-exitstatus=>2, -verbose=>0); +} + +# Check configuration +if ($Opt_Sp) { + (defined $ENV{SYSTEMC}) or die "%Error: verilator: Need \$SYSTEMC in environment\nProbably System-C isn't installed, see http://www.systemc.org\n"; +} +if ($Opt_Sp eq 'sp' || $Opt_Trace) { + if (!defined $ENV{SYSTEMPERL}) { + my $try = "$ENV{W}/hw/utils/perltools/SystemC/src"; + $ENV{SYSTEMPERL} = $try if -d $try; + } + (defined $ENV{SYSTEMPERL}) or die "%Error: verilator: Need \$SYSTEMPERL in environment for --sp or --trace\nProbably System-Perl isn't installed, see http://www.veripool.com/systemperl.html\n"; +} + +# Determine runtime flags +my $vcmd =(($opt_gdb?"$opt_gdb ":"") + .verilator_bin() + ." --bin ".verilator_bin() # So we get binary name into .d file + ." ".join(' ',@Opt_Verilator_Sw) + ); + +# Run verilator +run ($vcmd); + +#---------------------------------------------------------------------- + +sub usage { + bin_version(); + print '$Revision$$Date$ ', "\n"; + pod2usage(-exitstatus=>2, -verbose=>2); +} + +sub debug { + shift; + my $level = shift; + $Debug = $level||3; +} + +sub version { + bin_version(); + exit (0); +} + +sub bin_version { + ($ENV{VERILATOR_ROOT}) or print "%Warning: Unknown rev: VERILATOR_ROOT undefined\n"; + run (verilator_bin()." --version"); +} + +####################################################################### +####################################################################### +# Builds + +sub verilator_bin { + my $bin = "$ENV{VERILATOR_ROOT}/".($ENV{VERILATOR_BIN}||"verilator_bin"); + if ($Debug && -x "${bin}_dbg") { $bin = "${bin}_dbg"; } + return $bin; +} + +####################################################################### +####################################################################### +# Utilities + +sub run { + # Run command, check errors + my $command = shift; + print "\t$command\n" if $Debug>=3; + system($command); + my $status = $?; + if ($status) { + if ($Debug) { # For easy rerunning + warn "%Error: export VERILATOR_ROOT=$ENV{VERILATOR_ROOT}\n"; + warn "%Error: $command\n"; + } + die "%Error: Command Failed $command, $status, stopped"; + } +} + +####################################################################### +####################################################################### +package main; +__END__ + +=pod + +=head1 NAME + +Verilator - Convert Verilog code to C++/SystemC + +=head1 SYNOPSIS + + verilator --help + verilator --version + verilator --cc [options] [top_level.v] [opt_c_files.cpp/c/cc] + verilator --sc [options] [top_level.v] [opt_c_files.cpp/c/cc] + verilator --sp [options] [top_level.v] [opt_c_files.cpp/c/cc] + +=head1 DESCRIPTION + +Verilator converts synthesizable (not behavioral) Verilog code, plus some +Synthesis, SystemVerilog and Sugar/PSL assertions, into C++, SystemC or +SystemPerl code. It is not a complete simulator, just a compiler. + +Verilator is invoked with parameters similar to GCC, Cadence +Verilog-XL/NC-Verilog, or Synopsys's VCS. It reads the specified Verilog +code, lints it, and optionally adds coverage and waveform tracing code. +For C++ and SystemC formats, it outputs .cpp and .h files. For SystemPerl +format, it outputs .sp files for the SystemPerl preprocessor, which greatly +simplifies writing SystemC code and is available at +L. + +The files created by Verilator are then compiled with C++. The user writes +a little C++ wrapper file, which instantiates the top level module, and +passes this filename on the command line. These C files are compiled in +C++, and linked with the Verilated files. + +The resulting executable will perform the actual simulation. + +=head1 ARGUMENTS + +=over 4 + +=item {file.v} + +Specifies the Verilog file containing the top module to be Verilated. + +=item {file.c/cc/cpp} + +Specifies optional C++ files to be linked in with the Verilog code. If any +C++ files are specified in this way, Verilator will include a make rule +that generates a I executable. Without any C++ files, Verilator +will stop at the I__ALL.a library, and presume you'll continue +linking with make rules you write yourself. + +=item --assert + +Enable all assertions, includes enabling the --psl flag. (If psl is not +desired, but other assertions are, use --assert --nopsl.) + +=item --cc + +Specifies C++ without SystemC output mode; see also --sc and --sp. + +=item --coverage + +Enables all forms of coverage, alias for --coverage-line, --coverage-user + +=item --coverage-line + +Specifies basic block line coverage analysis code should be inserted. + +Coverage analysis adds statements at each code flow change point, which are +the branches of IF and CASE statements, a superset of normal Verilog Line +Coverage. At each such branch a C++ macro is called, which SystemPerl uses +to increment an array. At the end of a test, logs/coverage.pl is written +with the data from the array. + +After running multiple tests, the vcoverage utility (from the SystemPerl +package) is executed. Vcoverage reads the logs/coverage.pl file(s), and +creates an annotated source code listing showing code coverage details. + +Verilator automatically disables coverage of branches that have a $stop in +them, as it is assumed $stop branches contain an error check that should +not occur. A /*verilator coverage_block_off*/ comment will perform a +similar function on any code in that block or below. + +For an example, after running 'make test' in the Verilator distribution, +see the test_sp/logs/coverage_source directory. Grep for lines starting +with '%' to see what lines Verilator believes need more coverage. + +=item --coverage-user + +Enables user inserted functional coverage. Currently, all functional +coverage points are specified using PSL which must be separately enabled +with --psl. + +For example, the following PSL statement will add a coverage point, with +the comment "DefaultClock": + + // psl default clock = posedge clk; + // psl cover {cyc==9} report "DefaultClock,expect=1"; + +=item --debug + +Select the debug built image of Verilator (if available), and enable +debugging messages and intermediate form dump files. + +=item -E + +Preprocess the source code, but do not compile, as with 'gcc -E'. Output +is written to standard out. Beware of enabling debugging messages, as they +will also go to standard out. + +=item --exe + +Generate a executable. You will also need to pass additional .cpp files on +the command line that implement the main loop for your simulation. + +=item --help + +Displays this message and program version and exits. + +=item --inline-mult I + +Tune the inlining of modules. The default value of 2000 specifies that up +to 2000 new operations may be added to the model by inlining, if more then +this number of operations would result, the module is not inlined. Larger +values, or -1 to inline everything, will lead to longer compile times, but +potentially faster runtimes. This setting is ignored for very small +modules; they will always be inlined, if allowed. + +=item --MMD + +Enable creation of .d dependency files, used for make dependency detection, +similar to gcc -MMD option. On by default, use --no-MMD to disable. + +=item --Mdir I + +Specifies the name of the Make object directory. All generated files will +be placed in this directory. + +=item --mod-prefix I + +Specifies the name to prepend to all lower level classes. Defaults to +the same as --prefix. + +=item --no-skip-identical + +Disables skipping execution of Verilator if all source files are identical, +and all output files exist with newer dates. + +=item -O0 + +Disables optimization of the model. + +=item -O3 + +Enables slow optimizations. This may reduce simulation runtimes at the +cost of compile time. This currently sets --inline-mult -1. + +=item -OI + +Enables or disables a specific optimizations, with the optimization +selected based on the letter passed. A lowercase letter disables an +optimization, an upper case letter enables it. This is intended for +debugging use only; see the source code for version-dependent mappings of +optimizations to -O letters. + +=item --output-split I + +Enables splitting the output .cpp/.sp files into multiple outputs. When a +C++ file exceeds the specified number of operations, a new file will be +created. In addition, any slow routines will be placed into __Slow files. +This accelerates compilation by as optimization can be disabled on the slow +routines, and the remaining files can be compiled on parallel machines. +With GCC 3.3 on Opteron, --output-split 20000 will result in splitting into +approximately one-minute-compile chunks. + +=item --pins64 + +Specifies SystemC outputs of 33-64 bits wide should use uint64_t instead of +the backward-compatible default of sc_bv's. + +=item --prefix I + +Specifies the name of the top level class. Defaults to the name of the top +level Verilog module. + +=item --profile-cfuncs + +Modify the created C++ functions to support profiling. The functions will +be minimized to contain one "basic" statement, generally a single always +block or wire statement. (Note this will slow down the executable by ~5%.) +Furthermore, the function name will be suffixed with the basename of the +Verilog module and line number the statement came from. This allows gprof +or oprofile reports to be correlated with the original Verilog source +statements. + +=item --private + +Opposite of --public. Is the default; this option exists for backwards +compatibility. + +=item --psl + +Enable PSL parsing. Without this switch, psl meta-comments are ignored. +See the --assert flag to enable all assertions, and --coverage-user to +enable functional coverage. + +=item --public + +Declares all signals and modules public. This will turn off signal +optimizations as if all signals had a /*verilator public*/ comments. This +will also turn off inlining as if all modules had a /*verilator +public_module*/, unless the module specifically enabled it with /*verilator +inline_module*/. + +=item --sc + +Specifies SystemC output mode; see also --cc and -sp. + +=item --sp + +Specifies SystemPerl output mode; see also --cc and -sc. + +=item --stats + +Creates a dump file with statistics on the design in {prefix}__stats.txt. + +=item --trace + +Adds waveform tracing code to the model. Having tracing compiled in may +result in some small performance losses, even when waveforms are not turned +on during model execution. + +=item --underline-zero + +Signals starting with a underline should be initialized to zero, as was +done in Verilator 2. Default is for all signals including those with +underlines being randomized. This option may be depreciated in future +versions. + +=item -Werror-I + +Convert the specified warning message into a error message. This is +generally to discourage users from violating important site-wide rules, for +example C<-Werror-NOUNOPTFLAT>. + +=item -Wno-I + +Disable the specified warning message. + +=item -x-assign 0 + +=item -x-assign 1 + +=item -x-assign unique (default) + +Controls the two-state value that is replaced when an assignment to X is +encountered. -x-assign=0 converts all Xs to 0s, this is the fastest. +-x-assign=1 converts all Xs to 1s, this is nearly as fast, but more likely +to find reset bugs as active high logic will fire. The default, +-x-assign=func will call a function to determine the value, this allows +randomization of all Xs and is the slowest, but safest. + +=back + +=head1 VERILOG ARGUMENTS + +The following arguments are compatible with GCC, VCS and most Verilog +programs. + +=over 4 + +=item +define+I+I + +=item -DI=I + +Defines the given preprocessor symbol. + +=item -f I + +Read the specified file, and act as if all text inside it was +specified as command line parameters. + +=item +incdir+I + +=item -II + +=item -y I + +Add the directory to the list of directories that should be searched +for include directories or libraries. + +=item +libext+I+I... + +Specify the extensions that should be used for finding modules. If for +example module I is referenced, look in I.I. + +=item -UI + +Undefines the given preprocessor symbol. + +=item -v I + +Read the filename as a Verilog library. Any modules in the file may be +used to resolve cell instantiations in the top level module, else ignored. + +=back + +=head1 EXAMPLE C++ EXECUTION + +We'll compile this example into C++. + + mkdir test_our + cd test_our + + cat <our.v + module our; + initial begin \$display("Hello World"); \$finish; end + endmodule + EOF + + cat <sim_main.cpp + #include "Vour.h" + #include "verilated.h" + int main(int argc, char **argv, char **env) { + Vour* top = new Vour; + while (!Verilated::gotFinish()) { top->eval(); } + } + EOF + +Now we run Verilator on our little example. + + export VERILATOR_ROOT=/path/to/where/verilator/was/installed + $VERILATOR_ROOT/bin/verilator --cc our.v --exe sim_main.cpp + +We can see the source code under the "obj_dir" directory. See the FILES +section below for descriptions of some of the files that were created. + + ls -l obj_dir + +We then can compile it + + cd obj_dir + make -f Vour.mk Vour + +(Verilator included a default compile rule and link rule, since we used +--exe and passed a .cpp file on the Verilator command line. You can also +write your own compile rules, as we'll show in the SYSTEMC section.) + +And now we run it + + cd .. + obj_dir/Vour + +And we get as output + + Hello World + - our.v:2: Verilog $finish + +Really, you're better off using a Makefile to do all this for you. Then, +when your source changes it will automatically run all of these steps. See +the test_c directory in the distribution for an example. + +=head1 EXAMPLE SYSTEMC EXECUTION + +This is an example similar to the above, but using SystemPerl. + + mkdir test_our_sc + cd test_our_sc + + cat <our.v + module our (clk); + input clk; // Clock is required to get initial activation + always @ (posedge clk) + begin \$display("Hello World"); \$finish; end + endmodule + EOF + + cat <sc_main.cpp + #include "Vour.h" + #include "systemperl.h" + int sc_main(int argc, char **argv) { + sc_clock clk ("clk",10, 0.5, 3, true); + Vour* top; + SP_CELL (top, Vour); + SP_PIN (top, clk, clk); + while (!Verilated::gotFinish()) { sc_start(1); } + } + EOF + +Now we run Verilator on our little example. + + export VERILATOR_ROOT=/path/to/where/verilator/was/installed + $VERILATOR_ROOT/bin/verilator --sp our.v + +Then we convert the SystemPerl output to SystemC. + + cd obj_dir + export SYSTEMPERL=/path/to/where/systemperl/kit/came/from + $SYSTEMPERL/sp_preproc --preproc *.sp + +(You can also skip the above sp_preproc by getting pure SystemC from +verilator by replacing the verilator --sp flag in the previous step with +-sc.) + +We then can compile it + + make -f Vour.mk Vour__ALL.a + make -f Vour.mk ../sc_main.o + make -f Vour.mk verilated.o + +And link with SystemC. Note your path to the libraries may vary, +depending on the operating system. + + export SYSTEMC=/path/to/where/systemc/was/built/or/installed + g++ -L$SYSTEMC/lib-linux ../sc_main.o Vour__ALL*.o verilated.o \ + -o Vour -lsystemc + +And now we run it + + cd .. + obj_dir/Vour + +And we get the same output as the C++ example: + + Hello World + - our.v:2: Verilog $finish + +Really, you're better off using a Makefile to do all this for you. Then, +when your source changes it will automatically run all of these steps. See +the test_sp directory in the distribution for an example. + +=head1 BENCHMARKING & OPTIMIZATION + +For best performance, run Verilator with the "-O3 -x-assign=0 --noassert" +flags. The -O3 flag will require longer compile times, and -x-assign=0 may +increase the risk of reset bugs in trade for performance; see the above +documentation for these flags. + +Minor Verilog code changes can also give big wins. You should not have any +UNOPTFLAT warnings from Verilator. Fixing these warnings can result in +huge improvements; one user fixed their one UNOPTFLAT warning by making a +simple change to a clock latch used to gate clocks and gained a 60% +performance improvement. + +Beyond that, the performance of a Verilated model depends mostly on your +C++ compiler and size of your CPU's caches. + +By default, the lib/verilated.mk file has optimization turned off. This is +for the benefit of new users, as it improves compile times at the cost of +runtimes. To add optimization as the default, set one of three variables, +OPT, OPT_FAST, or OPT_SLOW in lib/verilated.mk. Or, just for one run, pass +them on the command line to make: + + make OPT_FAST="-O2" -f Vour.mk Vour__ALL.a + +OPT_FAST specifies optimizations for those programs that are part of the +fast path, mostly code that is executed every cycle. OPT_SLOW specifies +optimizations for slow-path files (plus tracing), which execute only +rarely, yet take a long time to compile with optimization on. OPT +specifies overall optimization and affects all compiles, including those +OPT_FAST and OPT_SLOW affect. For best results, use OPT="-O2". Nearly the +same results can be had with much better compile times with OPT_FAST="-O1". +Unfortunately, using the optimizer with SystemC files can result in +compiles taking several minutes. (The SystemC libraries have many little +inlined functions that drive the compiler nuts.) + +For best results, use GCC 3.3 or newer. GCC 3.2 and earlier have +optimization bugs around pointer aliasing detection, which can result in 2x +performance losses. + +If you will be running many simulations on a single compile, investigate +feedback driven compilation. With GCC, using -fprofile-arcs, then +-fbranch-probabilities will yield another 15% or so. + +You may uncover further tuning possibilities by profiling the Verilog code. +Use Verilator's --profile-cfuncs, then GCC's -g -pg. You can then run +either oprofile or gprof to see where in the C++ code, and by looking at +the mangled function name where in the Verilog code most of the time is +being spent. + +When done, please let the author know the results. I like to keep tabs on +how Verilator compares, and may be able to suggest additional improvements. + +=head1 FILES + +Verilator creates the following files: + + {prefix}.mk // Make include file for compiling + {prefix}_classes.mk // Make include file with class names + +For -cc and -sc mode, it also creates: + + {prefix}.cpp // Top level C++ file + {prefix}.h // Top level header + {prefix}{each_verilog_module}.cpp // Lower level internal C++ files + {prefix}{each_verilog_module}.h // Lower level internal header files + +For -sp mode, it also creates: + + {prefix}.sp // Top level SystemC file + {prefix}{each_verilog_module}.sp // Lower level internal SC files + +In certain optimization modes, it also creates: + + {prefix}__Slow.cpp // Constructors and infrequent routines + {prefix}__Syms.cpp // Global symbol table C++ + {prefix}__Syms.h // Global symbol table header + {prefix}__Trace.cpp // Wave file generation code (--trace) + {prefix}__stats.txt // Statistics (--stats) + +It also creates internal files that can be mostly ignored: + + {each_verilog_module}.vpp // Post-processed verilog source code + {prefix}.flags_vbin // Verilator dependencies + {prefix}.flags_vpp // Pre-processor dependencies + {prefix}{misc}.d // Make dependencies (-MMD) + {prefix}{misc}.dot // Debugging graph files (--debug) + {prefix}{misc}.tree // Debugging files (--debug) + +After running Make, the compiler will produce the following: + {prefix} // Final executable (w/--exe argument) + {prefix}__ALL.a // Library of all Verilated objects + {prefix}{misc}.o // Intermediate objects + +=head1 ENVIRONMENT + +=over 4 + +=item SYSTEMC + +Required for SystemC output mode. If set, specifies the directory +containing the SystemC distribution. This is used to find the SystemC +include files. + +=item SYSTEMC_ARCH + +Specifies the architecture name used by the SystemC kit. This is the part +after the dash in the lib-{...} directory name created by a 'make' in the +SystemC distribution. If not set, Verilator will try to intuit the proper +setting. + +=item SYSTEMC_CXX_FLAGS + +Specifies additional flags that are required to be passed to GCC when +building the SystemC model. + +=item SYSTEMPERL + +Specifies the directory containing the Verilog-Perl distribution kit. This +is used to find the Verilog-Perl library and include files. + +=item VCS_HOME + +If set, specifies the directory containing the Synopsys VCS distribution. +When set, a 'make test' in the Verilator distribution will also run VCS +baseline regression tests. + +=item VERILATOR_BIN + +If set, specifies an alternative name of the Verilator binary. May be used +for debugging and selecting between multiple operating system builds. + +=item VERILATOR_ROOT + +Specifies the directory containing the distribution kit. This is used to +find the executable, Perl library, and include files. + +=back + +=head1 CONNECTING TO C++ + +Verilator creates a .h and .cpp file for the top level module and all +modules under it. See the test_c directory in the kit for an example. + +After the modules are completed, there will be a I.mk file that may +be used with Make to produce a I__ALL.a file with all required +objects in it. This is then linked with the user's top level to create the +simulation executable. + +The user must write the top level of the simulation. Here's a simple +example: + + #include // Defines common routines + #include "Vtop.h" // From Verilating "top.v" + + Vtop *top; // Instantiation of module + + unsigned int main_time = 0; // Current simulation time + + double sc_time_stamp () { // Called by $time in Verilog + return main_time; + } + + int main() { + top = new Vtop; // Create instance + + top->reset_l = 0; // Set some inputs + + while (!Verilated::gotFinish()) { + if (main_time > 10) { + top->reset_l = 1; // Deassert reset + } + if ((main_time % 10) == 1) { + top->clk = 1; // Toggle clock + } + if ((main_time % 10) == 6) { + top->clk = 0; + } + top->eval(); // Evaluate model + cout << top->out << endl; // Read a output + main_time++; // Time passes... + } + + top->final(); // Done simulating + // // (Though this example doesn't get here) + } + +Note signals are read and written as member variables of the lower module. +You call the eval() method to evaluate the model. When the simulation is +complete call the final() method to wrap up any SystemVerilog final blocks, +and complete any assertions. + +=head1 CONNECTING TO SYSTEMC + +Verilator will convert the top level module to a SC_MODULE. This module +will plug directly into a SystemC netlist. + +The SC_MODULE gets the same pinout as the Verilog module, with the +following type conversions: Pins of a single bit become bool, unless they +are marked with `systemc_clock, in which case they become sc_clock's (for +SystemC 1.2, not needed in SystemC 2.0). Pins 2-32 bits wide become +uint32_t's. Pins 33-64 bits wide become sc_bv's or uint64_t's depending on +the -pins64 switch. Wider pins become sc_bv's. + +Lower modules are not pure SystemC code. This is a feature, as using the +SystemC pin interconnect scheme everywhere would reduce performance by an +order of magnitude. + +=head1 VERILOG 2001 SUPPORT + +Verilator supports the more common Verilog 2001 language features. This +includes signed numbers, "always @*", comma separated sensitivity lists, +generate statements, multidimensional arrays, localparam, and C-style +declarations inside port lists. + +=head1 SYSTEMVERILOG 3.1 SUPPORT + +Verilator currently has very minimal support for SystemVerilog. + +Verilator implement the full SystemVerilog 3.1 preprocessor subset, +including function call-like preprocessor defines. Verilator also +implements $bits, $countones, $isunknown, $onehot, and $onehot0, +always_comb, always_ff, always_latch, and final. + +=head1 SUGAR/PSL SUPPORT + +With the --assert switch, Verilator is just beginning to support the +Property Specification Language (PSL), specifically the simple subset +without time-branching primitives. Verilator currently only converts PSL +assertions to simple "if (...) error" statements, and coverage statements +to increment the line counters described in the coverage section. If you +need additional features please contact the author. + +Verilator implements these keywords: assert, assume (same as assert), +default (for clocking), countones, cover, isunknown, onehot, onehot0, +report, true. + +Verilator implements these operators: -> (logical if). + +Verilator does not support SEREs yet. All assertion and coverage +statements must be simple expressions that complete in one cycle. +(Arguably SEREs are the whole point of PSL, but one must start somewhere.) + +PSL vmode/vprop/vunits are not supported. PSL statements must be in the +module they reference, at the module level where you would put an +initial... statement. + +Verilator only supports (posedge CLK) or (negedge CLK), where CLK is the +name of a one bit signal. You may not use arbitrary expressions as +assertion clocks. + +=head1 SYNTHESIS DIRECTIVE ASSERTIONS + +With the --assert switch, Verilator reads any "//synopsys full_case" or "// +synopsys parallel_case" directives. The same applies to any "//cadence" or +"// ambit synthesis" directives of the same form. + +When these synthesis directives are discovered, Verilator will either +formally prove the directive to be true, or failing that, will insert the +appropriate code to detect failing cases at runtime and print an "Assertion +failed" error message. + +=head1 LANGUAGE EXTENSIONS + +The following additional constructs are the extensions Verilator supports +on top of standard Verilog code. Using these features outside of comments +or `ifdef's may break other tools. + +=over 4 + +=item `__FILE__ + +The __FILE__ define expands to the current filename, like C++'s __FILE__. + +=item `__LINE__ + +The __LINE__ define expands to the current line number, like C++'s __LINE__. + +=item `error I + +This will report an error when encountered, like C++'s #error. + +=item _(I) + +A underline followed by an expression in parenthesis returns a Verilog +expression. This is different from normal parenthesis in special contexts, +such as PSL expressions, and can be used to embed bit concatenation ({}) +inside of PSL statements. + +=item $c(I, ...); + +The string will be embedded directly in the output C++ code at the point +where the surrounding Verilog code is compiled. It may either be a +standalone statement (with a trailing ; in the string), or a function that +returns up to a 32-bit number (without a trailing ;). This can be used to +call C++ functions from your Verilog code. + +String arguments will be put directly into the output C++ code. Expression +arguments will have the code to evaluate the expression inserted. Thus to +call a C++ function, $c("func(",a,")") will result in 'func(a)' in the +output C++ code. For input arguments, rather then hardcoding variable +names in the string $c("func(a)"), instead pass the variable as an +expression $c("func(",a,")"). This will allow the call to work inside +Verilog functions where the variable is flattened out, and also enable +other optimizations. + +If you will be reading or writing any Verilog variables inside the C++ +functions, the Verilog signals must be declared with /*verilator public*/. + +You may also append a arbitrary number to $c, generally the width of the +output. [signal_32_bits = $c32("...");] This allows for compatibility with +other simulators which require a differently named PLI function name for +each different output width. + +=item $display, $write, $fdisplay, $fwrite + +$display format arguments may use C fprintf sizes after the % escape. Per +the Verilog standard, %x prints a number with the natural width, %0x prints +a number with minimum width, however %5x prints 5 digits per the C standard +(it's unspecified in Verilog). + +=item $fopen, $fclose, $fdisplay, $fwrite + +File descriptors passed to the file PLI calls must be file descriptors, not +MCDs, which includes the mode parameter to $fopen being mandatory. +Verilator will convert the integer used to hold the file descriptor into a +internal FILE*. To prevent core dumps due to mis-use, and because integers +are 32 bits while FILE*s may be 64 bits, the descriptor must be stored in a +reg [63:0] rather then an integer. The define `verilator_file_descriptor in +verilated.v can be used to hide this difference. + +=item `coverage_block_off + +Specifies the entire begin/end block should be ignored for coverage analysis. +Same as /* verilator coverage_block_off */. + +=item `systemc_header + +Take remaining text up to the next `verilog or `systemc_... mode switch and +place it verbatim into the output .h file's header. Despite the name of this +macro, this also works in pure C++ code. + +=item `systemc_ctor + +Take remaining text up to the next `verilog or `systemc_... mode switch and +place it verbatim into the C++ class constructor. Despite the name of this +macro, this also works in pure C++ code. + +=item `systemc_interface + +Take remaining text up to the next `verilog or `systemc_... mode switch and +place it verbatim into the C++ class interface. Despite the name of this +macro, this also works in pure C++ code. + +=item `systemc_imp_header + +Take remaining text up to the next `verilog or `systemc_... mode switch and +place it verbatim into the header of all files for this C++ class +implementation. Despite the name of this macro, this also works in pure +C++ code. + +=item `systemc_implementation + +Take remaining text up to the next `verilog or `systemc_... mode switch and +place it verbatim into a single file of the C++ class implementation. +Despite the name of this macro, this also works in pure C++ code. + +If you will be reading or writing any Verilog variables in the C++ +functions, the Verilog signals must be declared with /*verilator public*/. +See also the public task feature; writing a accessor may result in cleaner +code. + +=item `verilator + +=item `verilator3 + +The verilator and verilator3 defines are set by default so you may `ifdef +around compiler specific constructs. + +=item `verilog + +Switch back to processing Verilog code, after a `systemc_... mode switch. + +=item /*verilator clock_enable*/ + +Experimental use only. Used after a signal declaration to indicate the +signal is used to gate a clock, and the user takes responsibility for +insuring there are no races related to it. (Typically by adding a latch, +and running static timing analysis.) This will cause the clock gate to be +ignored in the scheduling algorithm, improving performance. + +=item /*verilator coverage_block_off*/ + +Specifies the entire begin/end block should be ignored for coverage analysis. + +=item /*verilator inline_module*/ + +Specifies the module the comment appears in may be inlined into any modules +that use this module. This is useful to speed up simulation time with some +small loss of trace visibility and modularity. Note signals under inlined +submodules will be named I__DOT__I as C++ does not +allow "." in signal names. SystemPerl when tracing such signals will +replace the __DOT__ with the period. + +=item /*verilator lint_off I*/ + +Disable the specified warning message for any warnings following the comment. + +=item /*verilator lint_on I*/ + +Re-enable the specified warning message for any warnings following the comment. + +=item /*verilator sc_clock*/ + +Used after a input declaration to indicate the signal should be declared in +SystemC as a sc_clock instead of a bool. + +=item /*verilator tracing_off*/ + +Disable waveform tracing for all future signals that are declared in this +module. Often this is placed just after a primitive's module statement, so +that the entire module is not traced. + +=item /*verilator tracing_on*/ + +Re-enable waveform tracing for all future signals that are declared. + +=item /*verilator public*/ (variable) + +Used after a input, output, register, or wire declaration to indicate the +signal should be declared so that C code may read or write the value +of the signal. Set for all signals when using the --public switch. + +=item /*verilator public*/ (task/function) + +Used inside the declaration section of a function or task declaration to +indicate the function or task should be made into a C++ function, public to +outside callers. Public tasks will be declared as a void C++ function, +public functions will get the appropriate non-void (bool, uint32_t, etc) +return type. Any input arguments will become C++ arguments to the +function. Any output arguments will become C++ reference arguments. Any +local registers/integers will become function automatic variables on the +stack. + +Wide variables over 64 bits cannot be I/O to the module, to avoid exposing +complexities; generally the cleanest technique is to pass a word number to +the function, and do appropriate bit selection. + +This feature is still somewhat experimental. Generally, only the values of +stored state (flops) should be written, as the model will NOT notice +changes made to variables in these functions. (Same as when a signal is +declared public.) + +=item /*verilator public_module*/ + +Used after a module statement to indicate the module should not be inlined +(unless specifically requested) so that C code may access the module. +Verilator automatically sets this attribute when the module contains any +public signals or `systemc_ directives. Also set for all modules when +using the --public switch. + +=back + +=head1 LANGUAGE LIMITATIONS + +There are some limitations and lack of features relative to a commercial +simulator, by intent. User beware. + +It is strongly recommended you use a lint tool before running this program. +Verilator isn't designed to easily uncover common mistakes that a lint +program will find for you. + +=head2 Synthesis Subset + +Verilator supports only the Synthesis subset with a few minor additions +such as $stop, $finish and $display. That is, you cannot use hierarchical +references, events or similar features of the Verilog language. It also +simulates as Synopsys's Design Compiler would; namely a block of the form + + always @ (x) y = x & z; + +will recompute y when there is a change in x or a change in z, which is +what Design Compiler will synthesize. A compliant simulator would only +calculate y if x changes. (Use verilog-mode's /*AS*/ or Verilog 2001's +always @* to prevent these issues.) + +=head2 Dotted cross-hierarchy references + +Verilator supports dotted references to variables, functions and tasks in +different modules. However, references into named blocks and function-local +variables are not supported. + +=head2 Latches + +Verilator is optimized for edge sensitive (flop based) designs. It will +attempt to do the correct thing for latches, but most performance +optimizations will be disabled around the latch. + +=head2 Time + +All delays (#) are ignored, as they are in synthesis. + +=head2 Two State + +Verilator is a two state simulator, not a four state simulator. However, it +has two features which uncover most initialization bugs (including many that +a four state simulator will miss.) + +First, assigning a variable to a X will actually assign the variable to a +random value (see the -x-assign switch.) Thus if the value is actually +used, the random value should cause downstream errors. Integers also +randomize, even though the Verilog 2001 specification says they initialize +to zero. + +Identity comparisons (=== or !==) are converted to standard ==/!== when +neither side is a constant. This may make the expression result differ +from a four state simulator. + +All variables are initialized using a function. By running several +random simulation runs you can determine that reset is working correctly. +On the first run, the function initializes variables to zero. On the +second, have it initialize variables to one. On the third and following +runs have it initialize them randomly. If the results match, reset works. +(Note this is what the hardware will really do.) In practice, just setting +all variables to one at startup finds most problems. + +=head2 Tri/Inout + +As a 2 state compiler, tristate and inouts are not supported. +Traditionally only chip "cores" are Verilated, the pad rings have been +written by hand in C++. + +=head2 Functions & Tasks + +All functions and tasks will be inlined (will not become functions in C.) +The only support provided is for simple statements in tasks (which may +affect global variables). + +Recursive functions and tasks are not supported. All inputs and outputs +are automatic, as if they had the Verilog 2001 "automatic" keyword +prepended. (If you don't know what this means, Verilator will do what you +probably expect -- what C does. The default behavior of Verilog is +different.) + +=head2 Generate Statements + +All instantiations and variables in generate blocks will be placed under +hierarchy that has a name different from that required in the language +specification. + +=head2 Generated Clocks + +Verilator attempts to deal with generated clocks correctly, however new +cases may turn up bugs in the scheduling algorithm. The safest option is +to have all clocks as primary inputs to the model. + +=head2 Ranges must be big-bit-endian + +Bit ranges must be numbered with the MSB being numbered greater or the same +as the LSB. Little-bit-endian busses [0:15] are not supported as they +aren't easily made compatible with C++. + +=head2 32-Bit Divide + +The division and modulus operators are limited to 32 bits. This can be +easily fixed if someone contributes the appropriate wide-integer math +functions. + +=head2 Gate Primitives + +The 2-state gate primitives (and, buf, nand, nor, not, or, xnor, xor) are +directly converted to behavioral equivalents. The 3-state and MOS gate +primitives are not supported. Tables are not supported. + +=head2 Specify blocks + +All specify blocks and timing checks are ignored. + +=head2 Array Initialization + +When initializing an array, you need to use non-delayed assignments. This +is done in the interest of speed; if delayed assignments were used, the +simulator would have to copy large arrays every cycle. (In smaller loops, +loop unrolling allows the delayed assignment to work, though it's a bit +slower then a non-delayed assignment.) Here's an example + + always @ (posedge clk) + if (~reset_l) begin + for (i=0; i<`ARRAY_SIZE; i++) begin + array[i] = 0; // Non-delayed for verilator + end + +=head2 Array Out of Bounds + +Writing a memory element that is outside the bounds specified for the array +may cause a different memory element inside the array to be written +instead. For power-of-2 sized arrays, Verilator will give a width warning +and the address. For non-power-of-2-sizes arrays, index 0 will be written. + +Reading a memory element that is outside the bounds specified for the array +will give a width warning and wrap around the power-of-2 size. For +non-power-of-2 sizes, it will return a unspecified constant of the +appropriate width. + +=head1 ERRORS + +Warnings may be disabled in two ways. First, when the warning is +printed it will include a warning code. Simply surround the offending +line with a warn_off/warn_on pair: + + // verilator lint_off UNSIGNED + if (`DEF_THAT_IS_EQ_ZERO <= 3) $stop; + // verilator lint_on UNSIGNED + +They may also be globally disabled by invoking Verilator with the +C<-Wno-I> switch. This should be avoided, as it removes +all checking across the designs, and prevents other users from +compiling your code without knowing the magic set of disables needed +to successfully compile your design. + +Warnings: + +=over 4 + +=item CASEINCOMPLETE + +Warns that inside a case statement there is a stimulus pattern for which +there is no case item specified. This is bad style, if a case is +impossible, it's better to have a "default: $stop;" or just "default: ;" so +that any design assumption violations will be discovered in simulation. + +=item CASEOVERLAP + +Warns that inside a case statement you have case values which are detected +to be overlapping. This is bad style, as moving the order of case values +will cause different behavior. Generally the values can be respecified to +not overlap. + +=item CASEX + +Warns that it is simply better style to use casez, and C in place of +C's. See +L + +=item CMPCONST + +Warns that you are comparing a value in a way that will always be constant. +For example "X > 1" will always be true when X is a single bit wide. + +=item COMBDLY + +Warns that you have a delayed assignment inside of a combinatorial block. +Using delayed assignments in this way is considered bad form, and may lead +to the simulator not matching synthesis. If this message is suppressed, +Verilator, like synthesis, will convert this to a non-delayed assignment, +which may result in logic races or other nasties. See +L + +=item GENCLK + +Warns that the specified signal is generated, but is also being used as a +clock. Verilator needs to evaluate sequential logic multiple times in this +situation. In somewhat contrived cases having any generated clock can +reduce performance by almost a factor of two. For fastest results, +generate ALL clocks outside in C++/SystemC and make them primary inputs to +your Verilog model. (However once need to you have even one, don't sweat +additional ones.) + +=item IMPLICIT + +Warns that a wire is being implicitly declared (it is a single bit wide +output from a sub-module.) While legal in Verilog, implicit declarations +only work for single bit wide signals (not buses), do not allow using a +signal before it is implicitly declared by a cell, and can lead to dangling +nets. A better option is the /*AUTOWIRE*/ feature of Verilog-Mode for +Emacs, available from L + +=item MULTIDRIVEN + +Warns that the specified signal comes from multiple always blocks. This is +often unsupported by synthesis tools, and is considered bad style. It will +also cause longer runtimes due to reduced optimizations. + +=item UNDRIVEN + +Warns that the specified signal is never sourced. + +=item UNOPT + +Warns that due to some construct, optimization of the specified signal or +block is disabled. The construct should be cleaned up to improve runtime. + +A less obvious case of this is when a module instantiates two submodules. +Inside submodule A, signal I is input and signal O is output. Likewise in +submodule B, signal O is an input and I is an output. A loop exists and a +UNOPT warning will result if AI & AO both come from and go to combinatorial +blocks in both submodules, even if they are unrelated always blocks. This +affects performance because Verilator would have to evaluate each submodule +multiple times to stabilize the signals crossing between the modules. + +=item UNOPTFLAT + +Warns that due to some construct, optimization of the specified signal is +disabled. The signal specified includes a complete scope to the signal; it +may be only one particular usage of a multiply instantiated block. The +construct should be cleaned up to improve runtime; two times better +performance may be possible by fixing these warnings. + +Unlike UNOPT this occurs after netlist flattening, and indicates a more +basic problem, as the less obvious case described under UNOPT does not +apply. + +Often this is caused by logic that isn't truly circular as viewed by +synthesis which analyzes interconnection per-bit, but is circular to +simulation which analyzes per-bus: + + wire [2:0] x = {x[1:0],shift_in}; + +This statement needs to be evaluated multiple times, as a change in +"shift_in" requires "x" to be computed 3 times before it becomes stable. +For significantly better performance, split this into 2 separate signals: + + wire [2:1] x_21 = x[1:0]; + wire [0:0] x_0 = shift_in; + +=item UNSIGNED + +Warns that you are comparing a unsigned value in a way that implies it is +signed, for example "X < 0" will always be true when X is unsigned. + +=item UNUSED + +Warns that the specified signal is never sinked. This is a future message, +currently Verilator will not produce this warning. + +=item VARHIDDEN + +Warns that a task, function, or begin/end block is declaring a variable by +the same name as a variable in the upper level module or begin/end block +(thus hiding the upper variable from being able to be used.) Rename the +variable to avoid confusion when reading the code. + +=item WIDTH + +Warns that based on width rules of Verilog, two operands have different +widths. Verilator generally can intuit the common usages of widths, and +you shouldn't need to disable this message like you do with most lint +programs. Generally other then simple mistakes, you have two solutions: + +If it's a constant 0 that's 32 bits or less, simply leave it +unwidthed. Verilator considers zero to be any width needed. + +Concatenate leading zeros when doing arithmetic. In the statement + + wire [5:0] plus_one = from[5:0] + 6'd1 + carry[0]; + +The best fix, which clarifies intent and will also make all tools happy is: + + wire [5:0] plus_one = from[5:0] + 6'd1 + {5'd0,carry[0]}; + +=back + +The following describes the less obvious errors: + +=over 4 + +=item Internal Error + +This error should never occur first, though may occur if earlier warnings +or error messages have corrupted the program. If there are no other +warnings or errors, submit a bug report. + +=item Unsupported: .... + +This error indicates that you are using a Verilog language construct +that is not yet supported in Verilator. See the Limitations chapter. + +=item Verilated model didn't converge + +Verilator sometimes has to evaluate combinatorial logic multiple times, +usually around code where a UNOPT warning was issued, but disabled. For +example: + + always @ (a) b=~a; + always @ (b) a=b + +will toggle forever and thus the executable will give the didn't converge +error to prevent an infinite loop. To debug this, compile the Verilated +.cpp files with -DVL_DEBUG, then call Verilated::debug(1) in your main.cpp. +This will cause each module to print a message when it's invoked. From +that it should be obvious what routine(s) are part of the infinite loop. +Then in Gdb, add a break point at the routine entry point and "print *this" +on each loop so you can see what variables are changing each invocation. + +=back + +=head1 FAQ/FREQUENTLY ASKED QUESTIONS + +=over 4 + +=item Does it run under Windows? + +Yes, using Cygwin. Verilated output should also compile under Microsoft +Visual C++ Version 7 or newer, but this is not tested by the author. + +=item Can you provide binaries? + +At this time I'd prefer to get patches out quickly then have to generate +myriad binaries for many different OS flavors. People have generally +requested binaries when they are having problems with their C++ +compiler. Alas, binaries won't help this, as in the end a fully working C++ +compiler is required to compile the output of Verilator. + +=item How can it be faster then (name-the-simulator)? + +Generally, the implied part of the question is "... with all of their +manpower they can put into it." + +Most commercial simulators have to be Verilog compliant, meaning event +driven. This prevents them from being able to reorder blocks and make +netlist-style optimizations, which are where most of the gains come from. + +Non-compliance shouldn't be scary. Your synthesis program isn't compliant, +so your simulator shouldn't have to be -- and Verilator is closer to the +synthesis interpretation, so this is a good thing for getting working +silicon. + +=item How do I generate waveforms (traces)? + +Add the --trace switch to Verilator, and make sure the SystemPerl package +is installed (SystemC itself isn't required for C++ only tracing.) In your +top level C code, call Verilated::traceEverOn(true). + +Then, in SystemC mode, create a SpTraceFile object. For an example, see +the call to SpTraceFile in the test_sp/sc_main.cpp file of the +distribution. + +Or, in C++ mode, create a SpTraceVcdCFile object, and see +test_c/sim_main.cpp. + +=item Where is the translate_off command? (How do I ignore a construct?) + +Translate on/off pragmas are generally a bad idea, as it's easy to have +mismatched pairs, and you can't see what another tool sees by just +preprocessing the code. Instead, use the preprocessor; Verilator defines +the "verilator" define for you, so just wrap the code in a ifndef region: + + `ifndef verilator + Something_Verilator_Dislikes; + `endif + +=item How do I prevent my assertions from firing during reset? + +Call Verilated::assertOn(false) before you first call the model, then turn +it back on after reset. It defaults to true. When false, all assertions +controlled by --assert are disabled. + +=item Is the PLI supported? + +No. + +More specifically, the common PLI-ish calls $display, $finish, $stop, +$time, $write are converted to C++ equivalents. If you want something more +complex, since Verilator emits standard C++ code, you can simply write your +own C++ routines that can access and modify signal values without needing +any PLI interface code, and call it with $c("{any_c++_statement}"). + +=item How do I get faster build times? + +Between GCC 3.0 to 3.3, each compiled progressively slower, thus if you can +use GCC 2.95, or GCC 3.4 you'll have faster builds. Two ways to cheat are +to compile on parallel machines and avoid compilations altogether. See the +--output-split option, and the web for the ccache, distcc and icecream +packages, and the Make::Cache package available from +L. Make::Cache will skip GCC runs between +identical source builds, even across different users. + +=item Why do so many files need to recompile when I add a signal? + +Adding a new signal requires the symbol table to be recompiled. Verilator +uses one large symbol table, as that results in 2-3 less assembly +instructions for each signal access. This makes the execution time 10-15% +faster, but can result in more compilations when something changes. + +=item How do I access signals in C? + +First, declare the signals you will be accessing with a /*verilator +public*/ comment before the closing semicolon. Then scope into the C++ +class to read the value of the signal, as you would any other member variable. + +Signals are the smallest of 8 bit chars, 16 bit shorts, 32 bit longs, or 64 +bit long longs that fits the width of the signal. Generally, you can use +just uint32_t's for 1 to 32 bits, or uint64_t for 1 to 64 bits, and the +compiler will properly up-convert smaller entities. + +Signals wider then 64 bits are stored as an array of 32-bit uint32_t's. +Thus to read bits 31:0, access signal[0], and for bits 63:32, access +signal[1]. Unused bits (for example bit numbers 65-96 of a 65 bit vector) +will always be zero. if you change the value you must make sure to pack +zeros in the unused bits or core-dumps may result. (Because Verilator +strips array bound checks where it believes them to be unnecessary.) + +In the SYSTEMC example above, if you had in our.v: + + input clk /*verilator public*/; + +From the sc_main.cpp file, you'd then: + + #include "Vour.h" + #include "Vour_our.h" + cout << "clock is " << top->v.clk << endl; + +In this example, clk is a bool you can read or set as any other variable. +The value of normal signals may be set, though clocks shouldn't be changed +by your code or you'll get strange results. + +=back + +=head1 BUGS + +First, check the the coding limitations section. + +Next, try the --debug switch. This will enable additional internal +assertions, and may help identify the problem. + +Finally, reduce your code to the smallest possible routine that exhibits +the bug. Even better, create a test in the test_regress/t directory, as +follows: + + cd test_regress + cp -p t/t_EXAMPLE.pl t/t_BUG.pl + cp -p t/t_EXAMPLE.v t/t_BUG.v + +Edit t/t_BUG.pl to suit your example; you can do anything you want in the +Verilog code there; just make sure it retains the single clk input and no +outputs. Now, the following should fail: + + cd test_regress + t/t_BUG.pl + +Finally, Mail the bug report to C + +=head1 HISTORY + +Verilator was conceived in 1994 by Paul Wasson at the Core Logic Group +at Digital Equipment Corporation. The Verilog code that was converted +to C was then merged with a C based CPU model of the Alpha processor +and simulated in a C based environment called CCLI. + +In 1995 Verilator started being used also for Multimedia and Network +Processor development inside Digital. Duane Galbi took over active +development of Verilator, and added several performance enhancements. +CCLI was still being used as the shell. + +In 1998, through the efforts of existing DECies, mainly Duane Galbi, +Digital graciously agreed to release the source code. (Subject to the +code not being resold, which is compatible with the GNU Public +License.) + +In 2001, Wilson Snyder took the kit, and added a SystemC mode, and +called it Verilator2. This was the first packaged public release. + +In 2002, Wilson Snyder created Verilator3 by rewriting Verilator from +scratch in C++. This added many optimizations, yielding about a 2-5x +performance gain. + +Currently, various language features and performance enhancements are added +as the need arises. Verilator is now about 2x faster then in 2002, and +as fast as most popular commercial simulators. + +=head1 CONTRIBUTORS + +Many people have provided ideas and other assistance with Verilator. + +The major corporate sponsors of Verilator, by providing funds or equipment +grants, are Compaq Corporation, Digital Equipment Corporation, Maker +Communications, Sun Microsystems, Nauticus Networks, and SiCortex. + +The people who have contributed code or other major functionality are Paul +Wasson, Duane Galbi, and Wilson Snyder. Major testers include Jeff Dutton, +Ralf Karge and Wim Michiels. + +Some of the people who have provided ideas and feedback for Verilator +include Hans Van Antwerpen, Jens Arm, David Black, Gregg Bouchard, Chris +Boumenot, John Brownlee, Lauren Carlson, Robert A. Clark, John Deroo, Danny +Ding, Jeff Dutton, Eugen Fekete, Sam Gladstone, Thomas Hawkins, Mike Kagen, +Ralf Karge, Dan Katz, Sol Katzman, Gernot Koch, Steve Kolecki, Steve Lang, +Charlie Lind, Dan Lussier, Fred Ma, Wim Michiels, John Murphy, Richard +Myers, Paul Nitza, Lisa Noack, Renga Sundararajan, Shawn Wang, Greg Waters, +Eugene Weber, Leon Wildman, and Mat Zeno. + +=head1 DISTRIBUTION + +The latest version is available from L. + +Copyright 2003-2006 by Wilson Snyder. Verilator 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 + +Major concepts by Paul Wasson and Duane Galbi. + +=head1 SEE ALSO + +L, L, L + +=cut + +###################################################################### diff --git a/bin/verilator_difftree b/bin/verilator_difftree new file mode 100755 index 000000000..742a8d543 --- /dev/null +++ b/bin/verilator_difftree @@ -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 . 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///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. + +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 + +=head1 SEE ALSO + +C + +=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: diff --git a/bin/verilator_includer b/bin/verilator_includer new file mode 100755 index 000000000..54d86cdb1 --- /dev/null +++ b/bin/verilator_includer @@ -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" +} diff --git a/configure.in b/configure.in new file mode 100644 index 000000000..0e01bc3ac --- /dev/null +++ b/configure.in @@ -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([]) diff --git a/include/.cvsignore b/include/.cvsignore new file mode 100644 index 000000000..5ecf2901c --- /dev/null +++ b/include/.cvsignore @@ -0,0 +1 @@ +verilated.mk diff --git a/include/verilated.cpp b/include/verilated.cpp new file mode 100644 index 000000000..6c4522152 --- /dev/null +++ b/include/verilated.cpp @@ -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()<=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) { +} + +//=========================================================================== diff --git a/include/verilated.h b/include/verilated.h new file mode 100644 index 000000000..c627e43bf --- /dev/null +++ b/include/verilated.h @@ -0,0 +1,1341 @@ +// $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 all Verilated C files +/// +/// This file is included automatically by Verilator at the top of +/// all C++ files it generates. It contains standard macros and +/// classes required by the Verilated code. +/// +/// Code available from: http://www.veripool.com/verilator +/// +//************************************************************************* + + +#ifndef _VERILATED_H_ +#define _VERILATED_H_ 1 ///< Header Guard + +#include "verilatedos.h" + +#include +#include +#include +#include +#include +#include +using namespace std; + +//========================================================================= +// Basic types + +typedef uint8_t CData; ///< Verilated data, 1-8 bits +typedef uint16_t SData; ///< Verilated data, 9-16 bits +typedef uint32_t IData; ///< Verilated data, 17-32 bits +typedef vluint64_t QData; ///< Verilated data, 33-64 bits +typedef uint32_t WData; ///< Verilated data, >64 bits, as an array + +typedef const WData* WDataInP; ///< Array input to a function +typedef WData* WDataOutP; ///< Array output from a function + +class SpTraceVcd; +class SpTraceVcdCFile; + +//========================================================================= +/// Base class for all Verilated module classes + +class VerilatedModule { +private: + const char* m_name; ///< Module name + VerilatedModule() {} ///< N/A, always use named constructor below +public: + VerilatedModule(const char* name); ///< Create module with given hierarchy name + ~VerilatedModule() {} + const char* name() const { return m_name; } ///< Return name of module +}; + +//========================================================================= +// Declare nets + +#ifndef VL_SIG +# define VL_SIG8(name, msb,lsb) CData name ///< Declare signal, 1-8 bits +# define VL_SIG16(name, msb,lsb) SData name ///< Declare signal, 9-16 bits +# define VL_SIG64(name, msb,lsb) QData name ///< Declare signal, 33-64 bits +# define VL_SIG(name, msb,lsb) IData name ///< Declare signal, 17-32 bits +# define VL_SIGW(name, msb,lsb, words) WData name[words] ///< Declare signal, 65+ bits +# define VL_IN8(name, msb,lsb) CData name ///< Declare input signal, 1-8 bits +# define VL_IN16(name, msb,lsb) SData name ///< Declare input signal, 9-16 bits +# define VL_IN64(name, msb,lsb) QData name ///< Declare input signal, 33-64 bits +# define VL_IN(name, msb,lsb) IData name ///< Declare input signal, 17-32 bits +# define VL_INW(name, msb,lsb, words) WData name[words] ///< Declare input signal, 65+ bits +# define VL_INOUT8(name, msb,lsb) CData name ///< Declare bidir signal, 1-8 bits +# define VL_INOUT16(name, msb,lsb) SData name ///< Declare bidir signal, 9-16 bits +# define VL_INOUT64(name, msb,lsb) QData name ///< Declare bidir signal, 33-64 bits +# define VL_INOUT(name, msb,lsb) IData name ///< Declare bidir signal, 17-32 bits +# define VL_INOUTW(name, msb,lsb, words) WData name[words] ///< Declare bidir signal, 65+ bits +# define VL_OUT8(name, msb,lsb) CData name ///< Declare output signal, 1-8 bits +# define VL_OUT16(name, msb,lsb) SData name ///< Declare output signal, 9-16 bits +# define VL_OUT64(name, msb,lsb) QData name ///< Declare output signal, 33-64bits +# define VL_OUT(name, msb,lsb) IData name ///< Declare output signal, 17-32 bits +# define VL_OUTW(name, msb,lsb, words) WData name[words] ///< Declare output signal, 65+ bits + +# define VL_PIN_NOP(instname,pin,port) ///< Connect a pin, ala SP_PIN +# define VL_CELL(instname,type) ///< Declare a cell, ala SP_CELL + +/// Declare a module, ala SC_MODULE +# define VL_MODULE(modname) struct modname : public VerilatedModule + +/// Constructor, ala SC_CTOR +# define VL_CTOR(modname) modname(const char* __VCname="") + +/// Constructor declaration for C++, ala SP_CTOR_IMPL +# define VL_CTOR_IMP(modname) modname::modname(const char* __VCname) : VerilatedModule(__VCname) + +/// Constructor declaration for SystemC, ala SP_CTOR_IMPL +# define VL_SC_CTOR_IMP(modname) modname::modname(sc_module_name) + +#endif + +//=========================================================================== +/// Verilator global static information class + +struct Verilated { + // Extern Vars + // Below two are used as bool, but having as uint32_t avoids conversion time + static uint32_t s_coverageRequest; ///< Collect coverage info now +private: + static int s_randReset; ///< Random reset: 0=all 0s, 1=all 1s, 2=random + static int s_debug; ///< See accessors... only when VL_DEBUG set + static bool s_calcUnusedSigs; ///< Waves file on, need all signals calculated + static bool s_gotFinish; ///< A $finish statement executed + static bool s_assertOn; ///< Assertions are enabled +public: + /// Select initial value of otherwise uninitialized signals. + //// + /// 0 = Set to zeros + /// 1 = Set all bits to one + /// 2 = Randomize all bits + static void randReset(int val) { s_randReset=val; } + static int randReset() { return s_randReset; } ///< Return randReset value + + /// Enable debug of internal verilated code + static inline void debug(int level) { s_debug = level; } +#ifdef VL_DEBUG + static inline int debug() { return s_debug; } ///< Return debug value +#else + static inline int debug() { return 0; } ///< Constant 0 debug, so C++'s optimizer rips up +#endif + /// Internal: Create a new module name by concatenating two strings + static const char* catName(const char* n1, const char* n2); // Returns new'ed data + /// Enable calculation of unused signals + static void calcUnusedSigs(bool flag) { s_calcUnusedSigs=flag; } + static bool calcUnusedSigs() { return s_calcUnusedSigs; } ///< Return calcUnusedSigs value + /// Did the simulation $finish? + static void gotFinish(bool flag) { s_gotFinish=flag; } + static bool gotFinish() { return s_gotFinish; } ///< Return if got a $finish + /// Allow traces to at some point be enabled (disables some optimizations) + static void traceEverOn(bool flag) { + if (flag) { calcUnusedSigs(flag); } + } + /// Enable/disable assertions + static void assertOn(bool flag) { s_assertOn=flag; } + static bool assertOn() { return s_assertOn; } +}; + +//========================================================================= +// Extern functions -- User may override -- See verilated.cpp + +/// Routine to call for $finish +extern void vl_finish (const char* filename, int linenum, const char* hier); +/// Routine to call for $stop +extern void vl_stop (const char* filename, int linenum, const char* hier); +/// Routine to call for a couple of fatal messages +extern void vl_fatal (const char* filename, int linenum, const char* hier, + const char* msg); + +//========================================================================= +// Extern functions -- Init time only, so slow is fine + +extern IData VL_RAND_RESET_I(int obits); ///< Random reset a signal +extern QData VL_RAND_RESET_Q(int obits); ///< Random reset a signal +extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp); ///< Random reset a signal +extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp); ///< Zero reset a signal + +/// Return string with formated Verilog value +extern const char* VL_VALUE_FORMATTED_W(int obits, char fmt, bool drop0, WDataInP lwp); +extern const char* VL_VALUE_FORMATTED_Q(int obits, char fmt, bool drop0, QData ld); +inline const char* VL_VALUE_FORMATTED_I(int obits, char fmt, bool drop0, IData ld) { + return VL_VALUE_FORMATTED_Q(obits,fmt,drop0,ld); +} + +/// File I/O +extern QData VL_FOPEN_WI(int fnwords, WDataInP filename, IData mode); +extern QData VL_FOPEN_QI(QData filename, IData mode); +inline QData VL_FOPEN_II(IData filename, IData mode) { return VL_FOPEN_QI(filename,mode); } + +//========================================================================= +// Base macros + +/// Return true if data[bit] set +#define VL_BITISSET_I(data,bit) (data & (1UL<>VL_WORDSIZE); } + +// Use a union to avoid cast-to-different-size warnings +/// Return FILE* from QData +static inline FILE* VL_CVT_Q_FP(QData lhs) { union { FILE* fp; QData q; } u; u.q=lhs; return u.fp; } +/// Return QData from FILE* +static inline QData VL_CVT_FP_Q(FILE* fp) { union { FILE* fp; QData q; } u; u.q=0; u.fp=fp; return u.q; } + +// Sign extend such that if MSB set, we get ffff_ffff, else 0s +// (Requires clean input) +#define VL_SIGN_I(nbits,lhs) ((lhs) >> VL_BITBIT_I((nbits) - 1UL)) +#define VL_SIGN_Q(nbits,lhs) ((lhs) >> VL_BITBIT_Q((nbits) - VL_ULL(1))) +#define VL_SIGNONES_I(nbits,lhs) (-(VL_SIGN_I(nbits,lhs))) + +// Sign bit extended up to MSB, doesn't include unsigned portion +// Optimization bug in GCC 3.3 returns different bitmasks to later states for +static inline IData VL_EXTENDSIGN_I(int lbits, IData lhs) { return (-((lhs)&(1UL<<(lbits-1)))); } +static inline QData VL_EXTENDSIGN_Q(int lbits, QData lhs) { return (-((lhs)&(VL_ULL(1)<<(lbits-1)))); } + +//========================================================================= +// Pli macros + +/// Return current simulation time +#if defined(SYSTEMC_VERSION) && (SYSTEMC_VERSION>20011000) +# define VL_TIME_I(ign) ((IData)sc_time_stamp().to_default_time_units()) +# define VL_TIME_Q(ign) ((QData)sc_time_stamp().to_default_time_units()) +#else +# define VL_TIME_I(ign) ((IData)sc_time_stamp()) +# define VL_TIME_Q(ign) ((QData)sc_time_stamp()) +extern double sc_time_stamp(); +#endif + +/// Evaluate expression if debug enabled +#ifdef VL_DEBUG +# define VL_DEBUG_IF(text) {if (VL_UNLIKELY(Verilated::debug())) {text}} +#else +# define VL_DEBUG_IF(text) +#endif + +/// Collect coverage analysis for this line +#ifndef SP_AUTO_COVER3 +# define SP_AUTO_COVER3(what,file,line) +#endif + + +//========================================================================= +// Functional macros/routines +// These all take the form +// VL_func_IW(bits,bits,op,op) +// VL_func_WW(bits,bits,out,op,op) +// The I/W indicates if it's a integer or wide for the output and each operand. +// The bits indicate the bit width of the output and each operand. +// If wide output, a temporary storage location is specified. + +//=================================================================== +// SETTING OPERATORS + +// Output clean +// EMIT_RULE: VL_CLEAN: oclean=clean; obits=lbits; +#define VL_CLEAN_II(obits,lbits,lhs) ((lhs) & VL_MASK_I(obits)) +#define VL_CLEAN_QQ(obits,lbits,lhs) ((lhs) & VL_MASK_Q(obits)) + +// EMIT_RULE: VL_ASSIGNCLEAN: oclean=clean; obits==lbits; +#define VL_ASSIGNCLEAN_W(obits,owp,lwp) VL_CLEAN_WW(obits,obits,owp,lwp) +static inline WDataOutP VL_CLEAN_WW(int obits, int, WDataOutP owp, WDataInP lwp){ + int words = VL_WORDS_I(obits); + for (int i=0; (i < (words-1)); i++) owp[i] = lwp[i]; + owp[words-1] = lwp[words-1] & VL_MASK_I(obits); + return(owp); +} + +// EMIT_RULE: VL_ASSIGN: oclean=rclean; obits==lbits; +// For now, we always have a clean rhs. +// Note: If a ASSIGN isn't clean, use VL_ASSIGNCLEAN instead to do the same thing. +static inline WDataOutP VL_ASSIGN_W(int obits, WDataOutP owp,WDataInP lwp){ + int words = VL_WORDS_I(obits); + for (int i=0; i < words; i++) owp[i] = lwp[i]; + return(owp); +} + +// EMIT_RULE: VL_ASSIGNBIT: rclean=clean; +static inline void VL_ASSIGNBIT_II(int, int bit, CData& lhsr, IData rhs) { + lhsr = ((lhsr & ~(1UL< _bvtemp; \ + _bvtemp.set_word(0,rd); \ + _bvtemp.set_word(1,rd>>VL_WORDSIZE); \ + svar.write(_bvtemp); \ +} +#define VL_ASSIGN_QS(obits,od,svar) { \ + od = (((QData)svar.read().get_word(1))< _bvtemp; \ + for (int i=0; i < VL_WORDS_I(obits); i++) _bvtemp.set_word(i,rwp[i]); \ + svar.write(_bvtemp); \ +} +#define VL_ASSIGN_WS(obits,owp,svar) { \ + int words = VL_WORDS_I(obits); \ + for (int i=0; i < words; i++) owp[i] = svar.read().get_word(i); \ + owp[words-1] &= VL_MASK_I(obits); \ +} + +//=================================================================== +// Extending sizes + +// CAREFUL, we're width changing, so obits!=lbits + +// Right must be clean because otherwise size increase would pick up bad bits +// EMIT_RULE: VL_EXTEND: oclean=clean; rclean==clean; +#define VL_EXTEND_II(obits,lbits,lhs) ((lhs)) +#define VL_EXTEND_QI(obits,lbits,lhs) ((QData)(lhs)) +#define VL_EXTEND_QQ(obits,lbits,lhs) ((lhs)) + +static inline WDataOutP VL_EXTEND_WI(int obits, int, WDataOutP owp, IData ld) { + // Note for extracts that obits != lbits + owp[0] = ld; + for (int i=1; i < VL_WORDS_I(obits); i++) owp[i] = 0; + return(owp); +} +static inline WDataOutP VL_EXTEND_WQ(int obits, int, WDataOutP owp, QData ld) { + VL_SET_WQ(owp,ld); + for (int i=2; i < VL_WORDS_I(obits); i++) owp[i] = 0; + return(owp); +} +static inline WDataOutP VL_EXTEND_WW(int obits, int lbits, WDataOutP owp, WDataInP lwp) { + for (int i=0; i < VL_WORDS_I(lbits); i++) owp[i] = lwp[i]; + for (int i=VL_WORDS_I(lbits); i < VL_WORDS_I(obits); i++) owp[i] = 0; + return(owp); +} + +// EMIT_RULE: VL_EXTENDS: oclean=*dirty*; obits=lbits; +// Sign extension; output dirty +static inline IData VL_EXTENDS_II(int, int lbits, IData lhs) { + return VL_EXTENDSIGN_I(lbits,lhs) | lhs; +} +static inline QData VL_EXTENDS_QI(int, int lbits, QData lhs/*Q_as_need_extended*/) { + return VL_EXTENDSIGN_Q(lbits,lhs) | lhs; +} +static inline QData VL_EXTENDS_QQ(int, int lbits, QData lhs) { + return VL_EXTENDSIGN_Q(lbits,lhs) | lhs; +} + +static inline WDataOutP VL_EXTENDS_WI(int obits, int lbits, WDataOutP owp, IData ld) { + IData sign = VL_SIGNONES_I(lbits,ld); + owp[0] = ld | sign & ~VL_MASK_I(lbits); + for (int i=1; i < VL_WORDS_I(obits); i++) owp[i] = sign; + return(owp); +} +static inline WDataOutP VL_EXTENDS_WQ(int obits, int lbits, WDataOutP owp, QData ld) { + VL_SET_WQ(owp,ld); + IData sign = VL_SIGNONES_I(lbits,owp[1]); + owp[1] |= sign & ~VL_MASK_I(lbits); + for (int i=2; i < VL_WORDS_I(obits); i++) owp[i] = sign; + return(owp); +} +static inline WDataOutP VL_EXTENDS_WW(int obits, int lbits, WDataOutP owp, WDataInP lwp) { + for (int i=0; i < VL_WORDS_I(lbits)-1; i++) owp[i] = lwp[i]; + int lmsw=VL_WORDS_I(lbits)-1; + IData sign = VL_SIGNONES_I(lbits,lwp[lmsw]); + owp[lmsw] = lwp[lmsw] | sign & ~VL_MASK_I(lbits); + for (int i=VL_WORDS_I(lbits); i < VL_WORDS_I(obits); i++) owp[i] = sign; + return(owp); +} + +//=================================================================== +// REDUCTION OPERATORS + +// EMIT_RULE: VL_REDAND: oclean=clean; lclean==clean; obits=1; +#define VL_REDAND_II(obits,lbits,lhs) (lhs == VL_MASK_I(lbits)) +#define VL_REDAND_IQ(obits,lbits,lhs) (lhs == VL_MASK_Q(lbits)) +static inline IData VL_REDAND_IW(int, int lbits, WDataInP lwp) { + int words = VL_WORDS_I(lbits); + IData combine=lwp[0]; + for (int i=1; i < words-1; i++) combine &= lwp[i]; + combine &= ~VL_MASK_I(lbits) | lwp[words-1]; + return ((~combine)==0); +} + +// EMIT_RULE: VL_REDOR: oclean=clean; lclean==clean; obits=1; +#define VL_REDOR_I(lhs) (lhs!=0) +#define VL_REDOR_Q(lhs) (lhs!=0) +static inline IData VL_REDOR_W(int words, WDataInP lwp) { + IData equal=0; + for (int i=0; i < words; i++) equal |= lwp[i]; + return(equal!=0); +} + +// EMIT_RULE: VL_REDXOR: oclean=dirty; obits=1; +static inline IData VL_REDXOR_W(int words, WDataInP lwp) { + IData r = lwp[0]; + for (int i=1; i < words; i++) r ^= lwp[i]; + r=(r^(r>>1)); + r=(r^(r>>2)); + r=(r^(r>>4)); + r=(r^(r>>8)); + r=(r^(r>>16)); + return r; +} +static inline IData VL_REDXOR_2(IData r) { + r=(r^(r>>1)); + return r; +} +static inline IData VL_REDXOR_4(IData r) { + r=(r^(r>>1)); r=(r^(r>>2)); + return r; +} +static inline IData VL_REDXOR_8(IData r) { + r=(r^(r>>1)); r=(r^(r>>2)); r=(r^(r>>4)); + return r; +} +static inline IData VL_REDXOR_16(IData r) { + r=(r^(r>>1)); r=(r^(r>>2)); r=(r^(r>>4)); r=(r^(r>>8)); + return r; +} +static inline IData VL_REDXOR_32(IData r) { + r=(r^(r>>1)); r=(r^(r>>2)); r=(r^(r>>4)); r=(r^(r>>8)); r=(r^(r>>16)); + return r; +} +static inline IData VL_REDXOR_64(QData r) { + r=(r^(r>>1)); r=(r^(r>>2)); r=(r^(r>>4)); r=(r^(r>>8)); r=(r^(r>>16)); r=(r^(r>>32)); + return r; +} + +// EMIT_RULE: VL_COUNTONES_II: oclean = false; lhs clean +static inline IData VL_COUNTONES_I(IData lhs) { + IData r = lhs - ((lhs >> 1) & 033333333333) - ((lhs >> 2) & 011111111111); + r = (r + (r>>3)) & 030707070707; + r = (r + (r>>6)); + r = (r + (r>>12) + (r>>24)) & 077; + return r; +} +static inline IData VL_COUNTONES_Q(QData lhs) { + return VL_COUNTONES_I(lhs) + VL_COUNTONES_I(lhs>>32); +} +static inline IData VL_COUNTONES_W(int words, WDataInP lwp) { + IData r = 0; + for (int i=0; (i < words); i++) r+=VL_COUNTONES_I(lwp[i]); + return r; +} + +static inline IData VL_ONEHOT_I(IData lhs) { + return ((lhs & (lhs-1))==0 & lhs!=0); +} +static inline IData VL_ONEHOT_Q(QData lhs) { + return ((lhs & (lhs-1))==0 & lhs!=0); +} +static inline IData VL_ONEHOT_W(int words, WDataInP lwp) { + IData one=0; + for (int i=0; (i < words); i++) { + if (lwp[i]) { + if (one) return 0; + one = 1; + if (lwp[i] & (lwp[i]-1)) return 0; + } + } + return one; +} + +static inline IData VL_ONEHOT0_I(IData lhs) { + return ((lhs & (lhs-1))==0); +} +static inline IData VL_ONEHOT0_Q(QData lhs) { + return ((lhs & (lhs-1))==0); +} +static inline IData VL_ONEHOT0_W(int words, WDataInP lwp) { + bool one=false; + for (int i=0; (i < words); i++) { + if (lwp[i]) { + if (one) return 0; + one = true; + if (lwp[i] & (lwp[i]-1)) return 0; + } + } + return 1; +} + +//=================================================================== +// SIMPLE LOGICAL OPERATORS + +// EMIT_RULE: VL_AND: oclean=lclean||rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_AND_W(int words, WDataOutP owp,WDataInP lwp,WDataInP rwp){ + for (int i=0; (i < words); i++) owp[i] = (lwp[i] & rwp[i]); + return(owp); +} +// EMIT_RULE: VL_OR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_OR_W(int words, WDataOutP owp,WDataInP lwp,WDataInP rwp){ + for (int i=0; (i < words); i++) owp[i] = (lwp[i] | rwp[i]); + return(owp); +} +// EMIT_RULE: VL_CHANGEXOR: oclean=1; obits=32; lbits==rbits; +static inline IData VL_CHANGEXOR_W(int words, WDataInP lwp,WDataInP rwp){ + IData od; + for (int i=0; (i < words); i++) od |= (lwp[i] ^ rwp[i]); + return(od); +} +// EMIT_RULE: VL_XOR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_XOR_W(int words, WDataOutP owp,WDataInP lwp,WDataInP rwp){ + for (int i=0; (i < words); i++) owp[i] = (lwp[i] ^ rwp[i]); + return(owp); +} +// EMIT_RULE: VL_XNOR: oclean=dirty; obits=lbits; lbits==rbits; +static inline WDataOutP VL_XNOR_W(int words, WDataOutP owp,WDataInP lwp,WDataInP rwp){ + for (int i=0; (i < words); i++) owp[i] = (lwp[i] ^ ~rwp[i]); + return(owp); +} +// EMIT_RULE: VL_NOT: oclean=dirty; obits=lbits; +static inline WDataOutP VL_NOT_W(int words, WDataOutP owp,WDataInP lwp) { + for (int i=0; i < words; i++) owp[i] = ~(lwp[i]); + return(owp); +} + +//========================================================================= +// Logical comparisons + +// EMIT_RULE: VL_EQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_NEQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_LT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_GT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_GTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_LTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +#define VL_NEQ_W(words,lwp,rwp) (!VL_EQ_W(words,lwp,rwp)) +#define VL_LT_W(words,lwp,rwp) (_VL_CMP_W(words,lwp,rwp)<0) +#define VL_LTE_W(words,lwp,rwp) (_VL_CMP_W(words,lwp,rwp)<=0) +#define VL_GT_W(words,lwp,rwp) (_VL_CMP_W(words,lwp,rwp)>0) +#define VL_GTE_W(words,lwp,rwp) (_VL_CMP_W(words,lwp,rwp)>=0) + +// Output clean, AND MUST BE CLEAN +static inline IData VL_EQ_W(int words, WDataInP lwp, WDataInP rwp) { + int nequal=0; + for (int i=0; (i < words); i++) nequal |= (lwp[i] ^ rwp[i]); + return(nequal==0); +} + +// Internal usage +static inline int _VL_CMP_W(int words, WDataInP lwp, WDataInP rwp) { + for (int i=words-1; i>=0; --i) { + if (lwp[i] > rwp[i]) return 1; + if (lwp[i] < rwp[i]) return -1; + } + return(0); // == +} + +#define VL_LTS_IWW(obits,lbits,rbbits,lwp,rwp) (_VL_CMPS_W(lbits,lwp,rwp)<0) +#define VL_LTES_IWW(obits,lbits,rbits,lwp,rwp) (_VL_CMPS_W(lbits,lwp,rwp)<=0) +#define VL_GTS_IWW(obits,lbits,rbits,lwp,rwp) (_VL_CMPS_W(lbits,lwp,rwp)>0) +#define VL_GTES_IWW(obits,lbits,rbits,lwp,rwp) (_VL_CMPS_W(lbits,lwp,rwp)>=0) + +static inline IData VL_GTS_III(int, int lbits, int, IData lhs, IData rhs) { + // For lbits==32, this becomes just a single instruction, otherwise ~5. + // GCC 3.3.4 sign extension bugs on AMD64 architecture force us to use quad logic + vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); //Q for gcc + vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); //Q for gcc + return lhs_signed > rhs_signed; +} +static inline IData VL_GTS_IQQ(int, int lbits, int, QData lhs, QData rhs) { + vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed > rhs_signed; +} + +static inline IData VL_GTES_III(int, int lbits, int, IData lhs, IData rhs) { + vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); //Q for gcc + vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); //Q for gcc + return lhs_signed >= rhs_signed; +} +static inline IData VL_GTES_IQQ(int, int lbits, int, QData lhs, QData rhs) { + vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed >= rhs_signed; +} + +static inline IData VL_LTS_III(int, int lbits, int, IData lhs, IData rhs) { + vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); //Q for gcc + vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); //Q for gcc + return lhs_signed < rhs_signed; +} +static inline IData VL_LTS_IQQ(int, int lbits, int, QData lhs, QData rhs) { + vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed < rhs_signed; +} + +static inline IData VL_LTES_III(int, int lbits, int, IData lhs, IData rhs) { + vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); //Q for gcc + vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); //Q for gcc + return lhs_signed <= rhs_signed; +} +static inline IData VL_LTES_IQQ(int, int lbits, int, QData lhs, QData rhs) { + vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed <= rhs_signed; +} + +static inline int _VL_CMPS_W(int lbits, WDataInP lwp, WDataInP rwp) { + int words = VL_WORDS_I(lbits); + int i=words-1; + // We need to flip sense if negative comparison + IData lsign = VL_SIGN_I(lbits,lwp[i]); + IData rsign = VL_SIGN_I(lbits,rwp[i]); + if (!lsign && rsign) return 1; // + > - + if (lsign && !rsign) return -1; // - < + + for (; i>=0; --i) { + if (lwp[i] > rwp[i]) return 1; + if (lwp[i] < rwp[i]) return -1; + } + return(0); // == +} + +//========================================================================= +// Math + +// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean; +// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean; +// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean; +#define VL_DIV_I(lhs,rhs) (((rhs)==0)?0:(lhs)/(rhs)) +#define VL_DIV_Q(lhs,rhs) (((rhs)==0)?0:(lhs)/(rhs)) +#define VL_MODDIV_I(lhs,rhs) (((rhs)==0)?0:(lhs)%(rhs)) +#define VL_MODDIV_Q(lhs,rhs) (((rhs)==0)?0:(lhs)%(rhs)) + +static inline WDataOutP VL_ADD_W(int words, WDataOutP owp,WDataInP lwp,WDataInP rwp){ + QData carry = 0; + for (int i=0; i> VL_ULL(32)) & VL_ULL(0xffffffff); + } + return(owp); +} + +static inline WDataOutP VL_SUB_W(int words, WDataOutP owp,WDataInP lwp,WDataInP rwp){ + QData carry = 0; + for (int i=0; i> VL_ULL(32)) & VL_ULL(0xffffffff); + } + return(owp); +} + +// Optimization bug in GCC 2.96 and presumably all-pre GCC 3 versions need this workaround +#if 1 //defined(__GNUC__) && __GNUC__ < 3 +static inline IData VL_UNARYMIN_I(IData data) { return -data; } +static inline QData VL_UNARYMIN_Q(QData data) { return -data; } +#else +# define VL_UNARYMIN_I(IData data) (-(data)) +# define VL_UNARYMIN_Q(QData data) (-(data)) +#endif + +static inline WDataOutP VL_UNARYMIN_W(int words, WDataOutP owp,WDataInP lwp){ + QData carry = 0; + for (int i=0; i> VL_ULL(32)) & VL_ULL(0xffffffff); + } + return(owp); +} + +static inline WDataOutP VL_MUL_W(int words, WDataOutP owp,WDataInP lwp,WDataInP rwp){ + for (int i=0; i> VL_ULL(32)) & VL_ULL(0xffffffff); + } + } + } + // Last output word is dirty + return(owp); +} + +static inline IData VL_MULS_III(int,int lbits,int, IData lhs,IData rhs) { + vlsint32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs); + vlsint32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs); + return lhs_signed * rhs_signed; +} +static inline QData VL_MULS_QQQ(int,int lbits,int, QData lhs,QData rhs) { + vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed * rhs_signed; +} + +static inline WDataOutP VL_MULS_WWW(int,int lbits,int, WDataOutP owp,WDataInP lwp,WDataInP rwp){ + int words = VL_WORDS_I(lbits); + IData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here + IData rwstore[VL_MULS_MAX_WORDS]; + WDataInP lwusp = lwp; + WDataInP rwusp = rwp; + IData lneg = VL_SIGN_I(lbits,lwp[words-1]); + if (lneg) { // Negate lhs + lwusp = lwstore; + VL_UNARYMIN_W(words, lwstore, lwp); + lwstore[words-1] &= VL_MASK_I(lbits); // Clean it + } + IData rneg = VL_SIGN_I(lbits,rwp[words-1]); + if (rneg) { // Negate rhs + rwusp = rwstore; + VL_UNARYMIN_W(words, rwstore, rwp); + rwstore[words-1] &= VL_MASK_I(lbits); // Clean it + } + VL_MUL_W(words,owp,lwusp,rwusp); + owp[words-1] &= VL_MASK_I(lbits); // Clean. Note it's ok for the multiply to overflow into the sign bit + if ((lneg ^ rneg) & 1) { // Negate output (not using UNARYMIN, as owp==lwp) + QData carry = 0; + for (int i=0; i> VL_ULL(32)) & VL_ULL(0xffffffff); + } + //Not needed: owp[words-1] |= 1<0) power = power*power; + if (rhs & (VL_ULL(1)<0) power = power*power; + if (rhs & (VL_ULL(1)<>nbitsonright) & hinsmask); + } + } +} + +// INTERNAL: Stuff large LHS bit 0++ into OUTPUT at specified offset +// lwp may be "dirty" +static inline void _VL_INSERT_WW(int, WDataOutP owp, WDataInP lwp, int hbit, int lbit) { + int hoffset = hbit & VL_SIZEBITS_I; + int loffset = lbit & VL_SIZEBITS_I; + int lword = VL_BITWORD_I(lbit); + int words = VL_WORDS_I(hbit-lbit+1); + if (hoffset==VL_SIZEBITS_I && loffset==0) { + // Fast and common case, word based insertion + for (int i=0; i>nbitsonright; + IData od = d & ~linsmask | owp[oword] & linsmask; + if (oword==hword) owp[oword] = owp[oword] & ~hinsmask | od & hinsmask; + else owp[oword] = od; + } + } + } + } +} + +static inline void _VL_INSERT_WQ(int obits, WDataOutP owp, QData ld, int hbit, int lbit) { + WData lwp[2]; VL_SET_WQ(lwp,ld); + _VL_INSERT_WW(obits,owp,lwp,hbit,lbit); +} + +// EMIT_RULE: VL_REPLICATE: oclean=clean>width32, dirty<=width32; lclean=clean; rclean==clean; +// RHS MUST BE CLEAN CONSTANT. +#define VL_REPLICATE_IOI(obits,lbits,rbits, ld, rep) (-(ld)) // Iff lbits==1 +#define VL_REPLICATE_QOI(obits,lbits,rbits, ld, rep) (-((QData)ld)) // Iff lbits==1 + +static inline IData VL_REPLICATE_III(int, int lbits, int, IData ld, IData rep) { + IData returndata = ld; + for (unsigned i=1; i < rep; i++){ + returndata = returndata << lbits; + returndata |= ld; + } + return (returndata); +} +static inline QData VL_REPLICATE_QII(int, int lbits, int, IData ld, IData rep) { + QData returndata = ld; + for (unsigned i=1; i < rep; i++){ + returndata = returndata << lbits; + returndata |= (QData)ld; + } + return (returndata); +} +static inline WDataOutP VL_REPLICATE_WII(int obits, int lbits, int, WDataOutP owp, IData ld, IData rep) { + owp[0] = ld; + for (unsigned i=1; i < rep; i++){ + _VL_INSERT_WI(obits,owp,ld,i*lbits+lbits-1,i*lbits); + } + return(owp); +} +static inline WDataOutP VL_REPLICATE_WQI(int obits, int lbits, int, WDataOutP owp, QData ld, IData rep) { + VL_SET_WQ(owp,ld); + for (unsigned i=1; i < rep; i++){ + _VL_INSERT_WQ(obits,owp,ld,i*lbits+lbits-1,i*lbits); + } + return(owp); +} +static inline WDataOutP VL_REPLICATE_WWI(int obits, int lbits, int, WDataOutP owp, WDataInP lwp, IData rep) { + for (int i=0; i < VL_WORDS_I(lbits); i++) owp[i] = lwp[i]; + for (unsigned i=1; i < rep; i++){ + _VL_INSERT_WW(obits,owp,lwp,i*lbits+lbits-1,i*lbits); + } + return(owp); +} + +// Because concats are common and wide, it's valuable to always have a clean output. +// Thus we specify inputs must be clean, so we don't need to clean the output. +// Note the bit shifts are always constants, so the adds in these constify out. +// Casts required, as args may be 8 bit entities, and need to shift to appropriate output size +#define VL_CONCAT_III(obits,lbits,rbits,ld,rd) ((IData)(ld)<<(rbits) | (IData)(rd)) +#define VL_CONCAT_QII(obits,lbits,rbits,ld,rd) ((QData)(ld)<<(rbits) | (QData)(rd)) +#define VL_CONCAT_QIQ(obits,lbits,rbits,ld,rd) ((QData)(ld)<<(rbits) | (QData)(rd)) +#define VL_CONCAT_QQI(obits,lbits,rbits,ld,rd) ((QData)(ld)<<(rbits) | (QData)(rd)) +#define VL_CONCAT_QQQ(obits,lbits,rbits,ld,rd) ((QData)(ld)<<(rbits) | (QData)(rd)) + +static inline WDataOutP VL_CONCAT_WII(int obits,int lbits,int rbits,WDataOutP owp,IData ld,IData rd) { + owp[0] = rd; + for (int i=1; i < VL_WORDS_I(obits); i++) owp[i] = 0; + _VL_INSERT_WI(obits,owp,ld,rbits+lbits-1,rbits); + return(owp); +} +static inline WDataOutP VL_CONCAT_WWI(int obits,int lbits,int rbits,WDataOutP owp,WDataInP lwp, IData rd) { + owp[0] = rd; + for (int i=1; i < VL_WORDS_I(obits); i++) owp[i] = 0; + _VL_INSERT_WW(obits,owp,lwp,rbits+lbits-1,rbits); + return(owp); +} +static inline WDataOutP VL_CONCAT_WIW(int obits,int lbits,int rbits,WDataOutP owp,IData ld, WDataInP rwp) { + for (int i=0; i < VL_WORDS_I(rbits); i++) owp[i] = rwp[i]; + for (int i=VL_WORDS_I(rbits); i < VL_WORDS_I(obits); i++) owp[i] = 0; + _VL_INSERT_WI(obits,owp,ld,rbits+lbits-1,rbits); + return(owp); +} +static inline WDataOutP VL_CONCAT_WIQ(int obits,int lbits,int rbits,WDataOutP owp,IData ld,QData rd) { + VL_SET_WQ(owp,rd); + for (int i=2; i < VL_WORDS_I(obits); i++) owp[i] = 0; + _VL_INSERT_WI(obits,owp,ld,rbits+lbits-1,rbits); + return(owp); +} +static inline WDataOutP VL_CONCAT_WQI(int obits,int lbits,int rbits,WDataOutP owp,QData ld,IData rd) { + owp[0] = rd; + for (int i=1; i < VL_WORDS_I(obits); i++) owp[i] = 0; + _VL_INSERT_WQ(obits,owp,ld,rbits+lbits-1,rbits); + return(owp); +} +static inline WDataOutP VL_CONCAT_WQQ(int obits,int lbits,int rbits,WDataOutP owp,QData ld,QData rd) { + VL_SET_WQ(owp,rd); + for (int i=2; i < VL_WORDS_I(obits); i++) owp[i] = 0; + _VL_INSERT_WQ(obits,owp,ld,rbits+lbits-1,rbits); + return(owp); +} +static inline WDataOutP VL_CONCAT_WWQ(int obits,int lbits,int rbits,WDataOutP owp,WDataInP lwp, QData rd) { + VL_SET_WQ(owp,rd); + for (int i=2; i < VL_WORDS_I(obits); i++) owp[i] = 0; + _VL_INSERT_WW(obits,owp,lwp,rbits+lbits-1,rbits); + return(owp); +} +static inline WDataOutP VL_CONCAT_WQW(int obits,int lbits,int rbits,WDataOutP owp,QData ld, WDataInP rwp) { + for (int i=0; i < VL_WORDS_I(rbits); i++) owp[i] = rwp[i]; + for (int i=VL_WORDS_I(rbits); i < VL_WORDS_I(obits); i++) owp[i] = 0; + _VL_INSERT_WQ(obits,owp,ld,rbits+lbits-1,rbits); + return(owp); +} +static inline WDataOutP VL_CONCAT_WWW(int obits,int lbits,int rbits,WDataOutP owp,WDataInP lwp, WDataInP rwp) { + for (int i=0; i < VL_WORDS_I(rbits); i++) owp[i] = rwp[i]; + for (int i=VL_WORDS_I(rbits); i < VL_WORDS_I(obits); i++) owp[i] = 0; + _VL_INSERT_WW(obits,owp,lwp,rbits+lbits-1,rbits); + return(owp); +} + +//=================================================================== +// Shifts + +// EMIT_RULE: VL_SHIFTL: oclean=lclean; rclean==clean; +// Important: Unlike most other funcs, the shift might well be a computed +// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) +static inline WDataOutP VL_SHIFTL_WWI(int obits,int,int,WDataOutP owp,WDataInP lwp, IData rd) { + int word_shift = VL_BITWORD_I(rd); + int bit_shift = VL_BITBIT_I(rd); + if ((int)rd >= obits) { + for (int i=0; i < VL_WORDS_I(obits); i++) owp[i] = 0; + } else if (bit_shift==0) { // Aligned word shift (<<0,<<32,<<64 etc) + for (int i=0; i < word_shift; i++) owp[i] = 0; + for (int i=word_shift; i < VL_WORDS_I(obits); i++) owp[i] = lwp[i-word_shift]; + } else { + for (int i=0; i < VL_WORDS_I(obits); i++) owp[i] = 0; + _VL_INSERT_WW(obits,owp,lwp,obits-1,rd); + } + return(owp); +} + +// EMIT_RULE: VL_SHIFTR: oclean=lclean; rclean==clean; +// Important: Unlike most other funcs, the shift might well be a computed +// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) +static inline WDataOutP VL_SHIFTR_WWI(int obits,int,int,WDataOutP owp,WDataInP lwp, IData rd) { + int word_shift = VL_BITWORD_I(rd); // Maybe 0 + int bit_shift = VL_BITBIT_I(rd); + if ((int)rd >= obits) { + for (int i=0; i < VL_WORDS_I(obits); i++) owp[i] = 0; + } else if (bit_shift==0) { // Aligned word shift (>>0,>>32,>>64 etc) + int copy_words = (VL_WORDS_I(obits)-word_shift); + for (int i=0; i < copy_words; i++) owp[i] = lwp[i+word_shift]; + for (int i=copy_words; i < VL_WORDS_I(obits); i++) owp[i] = 0; + } else { + int loffset = rd & VL_SIZEBITS_I; + int nbitsonright = 32-loffset; // bits that end up in lword (know loffset!=0) + // Middle words + int words = VL_WORDS_I(obits-rd); + for (int i=0; i>loffset; + int upperword = i+word_shift+1; + if (upperword < VL_WORDS_I(obits)) { + owp[i] |= lwp[upperword]<< nbitsonright; + } + } + for (int i=words; i> operator as a arithmetic shift! + IData sign = -(lhs >> (obits-1)); // ffff_ffff if negative + IData signext = ~(VL_MASK_I(obits) >> rhs); // One with bits where we've shifted "past" + return (lhs >> rhs) | sign & VL_CLEAN_II(obits,obits,signext); +} +static inline QData VL_SHIFTRS_QQI(int obits, int, int, QData lhs, IData rhs) { + QData sign = -(lhs >> (obits-1)); + QData signext = ~(VL_MASK_Q(obits) >> rhs); + return (lhs >> rhs) | sign & VL_CLEAN_QQ(obits,obits,signext); +} +static inline WDataOutP VL_SHIFTRS_WWI(int obits,int,int,WDataOutP owp,WDataInP lwp, IData rd) { + int word_shift = VL_BITWORD_I(rd); + int bit_shift = VL_BITBIT_I(rd); + int lmsw = VL_WORDS_I(obits)-1; + IData sign = VL_SIGNONES_I(obits,lwp[lmsw]); + if ((int)rd >= obits) { + for (int i=0; i <= lmsw; i++) owp[i] = sign; + } else if (bit_shift==0) { // Aligned word shift (>>0,>>32,>>64 etc) + int copy_words = (VL_WORDS_I(obits)-word_shift); + for (int i=0; i < copy_words; i++) owp[i] = lwp[i+word_shift]; + if (copy_words>=0) owp[copy_words-1] |= ~VL_MASK_I(obits) & sign; + for (int i=copy_words; i < VL_WORDS_I(obits); i++) owp[i] = sign; + } else { + int loffset = rd & VL_SIZEBITS_I; + int nbitsonright = 32-loffset; // bits that end up in lword (know loffset!=0) + // Middle words + int words = VL_WORDS_I(obits-rd); + for (int i=0; i>loffset; + int upperword = i+word_shift+1; + if (upperword < VL_WORDS_I(obits)) { + owp[i] |= lwp[upperword]<< nbitsonright; + } + } + if (words) owp[words-1] |= sign & ~VL_MASK_I(obits-loffset); + for (int i=words; i>(rhs)) +#define VL_BITSEL_QIII(obits,lbits,rbits,zbits,lhs,rhs) ((lhs)>>(rhs)) +#define VL_BITSEL_QQII(obits,lbits,rbits,zbits,lhs,rhs) ((lhs)>>(rhs)) +#define VL_BITSEL_IQII(obits,lbits,rbits,zbits,lhs,rhs) ((IData)((lhs)>>(rhs))) + +static inline IData VL_BITSEL_IWII(int, int lbits, int, int, WDataInP lwp, IData rd) { + int word = VL_BITWORD_I(rd); + if ((int)rd>lbits) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + // We return all 1's as that's more likely to find bugs (?) then 0's. + } else { + return (lwp[word]>>VL_BITBIT_I(rd)); + } +} + +// EMIT_RULE: VL_RANGE: oclean=lclean; out=dirty +// & MUST BE CLEAN (currently constant) +#define VL_SEL_IIII(obits,lbits,rbits,tbits,lhs,lsb,width) ((lhs)>>(lsb)) +#define VL_SEL_QQII(obits,lbits,rbits,tbits,lhs,lsb,width) ((lhs)>>(lsb)) +#define VL_SEL_IQII(obits,lbits,rbits,tbits,lhs,lsb,width) ((IData)((lhs)>>(lsb))) + +static inline IData VL_SEL_IWII(int, int lbits, int, int, WDataInP lwp, IData lsb, IData width) { + int msb = lsb+width-1; + if (msb>lbits) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + } else if (VL_BITWORD_I(msb)==VL_BITWORD_I((int)lsb)) { + return (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)); + } else { + // 32 bit extraction may span two words + int nbitsfromlow = 32-VL_BITBIT_I(lsb); // bits that come from low word + return ((lwp[VL_BITWORD_I(msb)]<>VL_BITBIT_I(lsb))); + } +} + +static inline QData VL_SEL_QWII(int, int lbits, int, int, WDataInP lwp, IData lsb, IData width) { + int msb = lsb+width-1; + if (msb>lbits) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + } else if (VL_BITWORD_I(msb)==VL_BITWORD_I((int)lsb)) { + return (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)); + } else if (VL_BITWORD_I(msb)==1+VL_BITWORD_I((int)lsb)) { + int nbitsfromlow = 32-VL_BITBIT_I(lsb); + QData hi = (lwp[VL_BITWORD_I(msb)]); + QData lo = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)); + return (hi<>VL_BITBIT_I(lsb)); + return (hi<<(nbitsfromlow+32)) | (mid<lbits) { // Outside bounds, + for (int i=0; i>loffset; + int upperword = i+word_shift+1; + if (upperword <= (int)VL_BITWORD_I(msb)) { + owp[i] |= lwp[upperword]<< nbitsfromlow; + } + } + for (int i=words; i= rhs width +static inline void VL_ASSIGNSEL_WIII(int obits, int lsb, WDataOutP owp, IData rhs) { + _VL_INSERT_WI(obits, owp, rhs, lsb+obits-1, lsb); +} +static inline void VL_ASSIGNSEL_WIIQ(int obits, int lsb, WDataOutP owp, QData rhs) { + _VL_INSERT_WQ(obits, owp, rhs, lsb+obits-1, lsb); +} +static inline void VL_ASSIGNSEL_WIIW(int obits, int lsb, WDataOutP owp, WDataInP rwp) { + _VL_INSERT_WW(obits, owp, rwp, lsb+obits-1, lsb); +} + +//====================================================================== +// Triops + +static inline WDataOutP VL_COND_WIWW(int obits, int, int, int, + WDataOutP owp, int cond, WDataInP w1p, WDataInP w2p) { + int words = VL_WORDS_I(obits); + for (int i=0; i < words; i++) owp[i] = cond ? w1p[i] : w2p[i]; + return(owp); +} + +//====================================================================== +// Constification + +// VL_CONST_W_#X(int obits, WDataOutP owp, IData data0, .... IData data(#-1)) +// Sets wide vector words to specified constant words, zeros upper data. + +#define I IData +#define _END(obits,wordsSet) \ + for(int i=(wordsSet);i $@ + $(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 diff --git a/include/verilated.v b/include/verilated.v new file mode 100644 index 000000000..206fa022e --- /dev/null +++ b/include/verilated.v @@ -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 diff --git a/include/verilatedos.h b/include/verilatedos.h new file mode 100644 index 000000000..797cf5621 --- /dev/null +++ b/include/verilatedos.h @@ -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 +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 +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*/ diff --git a/install-sh b/install-sh new file mode 100644 index 000000000..ebc66913e --- /dev/null +++ b/install-sh @@ -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 diff --git a/mkinstalldirs b/mkinstalldirs new file mode 100644 index 000000000..f82d8f1ee --- /dev/null +++ b/mkinstalldirs @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# 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 diff --git a/nodist/dot_pruner b/nodist/dot_pruner new file mode 100755 index 000000000..191a3835a --- /dev/null +++ b/nodist/dot_pruner @@ -0,0 +1,215 @@ +#!/usr/bin/perl -w +# $Id$ +###################################################################### +# +# 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 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 + +=head1 SEE ALSO + +=cut + +###################################################################### +### Local Variables: +### compile-command: "./dot_pruner | tee ~/d/a.dot" +### End: diff --git a/nodist/vtree_importer b/nodist/vtree_importer new file mode 100755 index 000000000..7b3b9a72c --- /dev/null +++ b/nodist/vtree_importer @@ -0,0 +1,362 @@ +#!/usr/bin/perl -w +# $Id$ +###################################################################### +# +# 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 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 + +=head1 SEE ALSO + +=cut + +###################################################################### +### Local Variables: +### compile-command: "./vtree_importer " +### End: diff --git a/readme.texi b/readme.texi new file mode 100644 index 000000000..bc6f301d6 --- /dev/null +++ b/readme.texi @@ -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. + diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 000000000..96b8cda8e --- /dev/null +++ b/src/.cvsignore @@ -0,0 +1,7 @@ +*.old +config.h +Makefile +Makefile_obj +.objcache* +obj_* +config_rev.h diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 000000000..fa80327a8 --- /dev/null +++ b/src/Makefile.in @@ -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* diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in new file mode 100644 index 000000000..87bbfec61 --- /dev/null +++ b/src/Makefile_obj.in @@ -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 + diff --git a/src/V3Active.cpp b/src/V3Active.cpp new file mode 100644 index 000000000..12a465c0a --- /dev/null +++ b/src/V3Active.cpp @@ -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 +#include +#include +#include +#include +#include + +#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 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 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::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 "<cloneTree(false)->castSenTree(); + activep = new AstActive(fl, "sequent", newsenp); + activep->sensesStorep(activep->sensesp()); + UINFO(8," New ACTIVE "<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 "<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 "<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 "<fileline()); + nodep->unlinkFrBack(); + wantactivep->addStmtsp(nodep); + } + virtual void visit(AstAssignAlias* nodep, AstNUser*) { + // Relink to CACTIVE, unless already under it + UINFO(4," ASSIGNW "<fileline()); + nodep->unlinkFrBack(); + wantactivep->addStmtsp(nodep); + } + virtual void visit(AstAssignW* nodep, AstNUser*) { + // Relink to CACTIVE, unless already under it + UINFO(4," ASSIGNW "<fileline()); + nodep->unlinkFrBack(); + wantactivep->addStmtsp(nodep); + } + virtual void visit(AstFinal* nodep, AstNUser*) { + // Relink to CFUNC for the final + UINFO(4," FINAL "<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 "<=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: " + <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__<<": "< +#include +#include +#include +#include + +#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 "<iterateChildren(*this); + } + virtual void visit(AstActive* nodep, AstNUser*) { + UINFO(4," ACTIVE "<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 "<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__<<": "< +#include +#include +#include +#include + +#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__<<": "< +#include +#include +#include +#include + +#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__<<": "< +#include +#include +#include +#include + +#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<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<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<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="<8) { os<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 logsp (V3File::new_ofstream(filename, append)); + if (logsp->fail()) v3fatalSrc("Can't write "<"; + *logsp<<" to "<castNetlist()) V3Broken::brokenAll(netp); + // Next dump can indicate start from here + editCountSetLast(); + } +} + +void AstNode::v3errorEnd(ostringstream& str) { + if (this && m_fileline) { + ostringstream nsstr; + nsstr<v3errorEnd(nsstr); + } else { + V3Error::v3errorEnd(str); + } +} + +//###################################################################### + +void AstNVisitor::doDeletes() { + for (vector::iterator it = m_deleteps.begin(); it != m_deleteps.end(); ++it) { + (*it)->deleteTree(); + } + m_deleteps.clear(); +} diff --git a/src/V3Ast.h b/src/V3Ast.h new file mode 100644 index 000000000..4afb7a31c --- /dev/null +++ b/src/V3Ast.h @@ -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 + +#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(_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<(_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(_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(_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(_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(_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<(_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< 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_bothcastInt(); } + 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 + 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(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 diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp new file mode 100644 index 000000000..9db071eae --- /dev/null +++ b/src/V3AstNodes.cpp @@ -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 +#include +#include +#include +#include +#include + +#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<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 vec; + for (AstSenItem* senp = sensesp(); senp; senp=senp->nextp()->castSenItem()) { + vec.push_back(senp); + } + sort(vec.begin(), vec.end(), AstSenItemCmp()); + for (vector::iterator it=vec.begin(); it!=vec.end(); ++it) { + (*it)->unlinkFrBack(); + } + for (vector::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<m_backp + <<" =editCountLast())?"#>":">") + <<" {"<lineno()<<"}" + <<" "<<(isSigned()?"s":"") + <<"w"<<(widthSized()?"":"u")<AstNode::dump(str); + str<<" sz"<AstNode::dump(str); + if (modp()) { str<<" -> "; modp()->dump(str); } + else { str<<" ->UNLINKED:"<AstNode::dump(str); + str<<" -> "<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<dump(str); } + else if (varp()) { varp()->dump(str); } + else { str<<"UNLINKED"; } +} +void AstModule::dump(ostream& str) { + this->AstNode::dump(str); + str<<" L"<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<<" "<AstNode::dump(str); + if (isMulti()) str<<" [MULTI]"; +} +void AstSenItem::dump(ostream& str) { + this->AstNode::dump(str); + str<<" ["<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<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<<" "<name()<<" => "; + funcp()->dump(str); + } +} diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h new file mode 100644 index 000000000..e6673eae0 --- /dev/null +++ b/src/V3AstNodes.h @@ -0,0 +1,2976 @@ +// $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 _V3ASTNODES_H_ +#define _V3ASTNODES_H_ 1 + +#ifndef _V3AST_H_ +#error "Use V3Ast.h as the include" +#endif + +//###################################################################### +//=== Ast* : Specific types +// Netlist interconnect + +struct AstConst : public AstNodeMath { + // A constant +private: + V3Number m_num; // Constant value +public: + AstConst(FileLine* fl, const V3Number& num) + :AstNodeMath(fl) + ,m_num(num) { + width(m_num.width(), m_num.sized()?0:m_num.minWidth()); + isSigned(m_num.isSigned()); + } + AstConst(FileLine* fl, uint32_t num) + :AstNodeMath(fl) + ,m_num(V3Number(fl,32,num)) { width(m_num.width(), m_num.sized()?0:m_num.minWidth()); } + virtual ~AstConst() {} + virtual AstType type() const { return AstType::CONST;} + virtual AstNode* clone() { return new AstConst(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string name() const { return num().ascii(); } // * = Value + virtual const V3Number& num() const { return m_num; } // * = Value + uint32_t asInt() const { return num().asInt(); } + vluint64_t asQuad() const { return num().asQuad(); } + virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially + virtual string emitOperator() { V3ERROR_NA; return ""; } + virtual bool cleanOut() { return true; } + virtual V3Hash sameHash() const { return V3Hash(num().asHash()); } + virtual bool same(AstNode* samep) const { + return num().isCaseEq(samep->castConst()->num()); } + virtual int instrCount() const { return widthInstrs(); } +}; + +struct AstRange : public AstNode { + // Range specification, for use under variables and cells + AstRange(FileLine* fl, AstNode* msbp, AstNode* lsbp) + :AstNode(fl) { + setOp2p(msbp); setOp3p(lsbp); } + AstRange(FileLine* fl, int msb, int lsb) + :AstNode(fl) { + setOp2p(new AstConst(fl,msb)); setOp3p(new AstConst(fl,lsb)); + width(msb-lsb+1,msb-lsb+1); + } + virtual ~AstRange() {} + virtual AstType type() const { return AstType::RANGE;} + virtual AstNode* clone() { return new AstRange(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstRange* cloneTree(bool cloneNextLink) { return AstNode::cloneTree(cloneNextLink)->castRange(); } + AstNode* msbp() const { return op2p()->castNode(); } // op2 = Msb expression + AstNode* lsbp() const { return op3p()->castNode(); } // op3 = Lsb expression + int msbConst() const { AstConst* constp=msbp()->castConst(); return (constp?constp->asInt():0); } + int lsbConst() const { AstConst* constp=lsbp()->castConst(); return (constp?constp->asInt():0); } + int elementsConst() const { return msbConst()-lsbConst()+1; } + virtual string emitOperator() { V3ERROR_NA; return ""; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + +struct AstArraySel : public AstNodeSel { + // Parents: math|stmt + // Children: varref|arraysel, math + AstArraySel(FileLine* fl, AstNode* fromp, AstNode* bitp) + :AstNodeSel(fl, fromp, bitp) { + if (fromp) widthSignedFrom(fromp); + } + virtual ~AstArraySel() {} + virtual AstType type() const { return AstType::ARRAYSEL;} + virtual AstNode* clone() { return new AstArraySel(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { + V3ERROR_NA; /* How can from be a const? */ } + virtual string emitVerilog() { return "%k(%l%k[%r])"; } + virtual string emitOperator() { V3ERROR_NA; return ""; } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } + virtual int instrCount() const { return widthInstrs(); } + // Special operators + static int dimension(AstNode* nodep) { ///< How many dimensions is this reference from the base variable? + int dim = 0; + while (nodep && nodep->castArraySel()) { dim++; nodep=nodep->castArraySel()->fromp(); } + return dim; + } + static AstNode* baseFromp(AstNode* nodep) { ///< What is the base variable (or const) this dereferences? + while (nodep && nodep->castArraySel()) { nodep=nodep->castArraySel()->fromp(); } + return nodep; + } +}; + +struct AstWordSel : public AstNodeSel { + // Select a single word from a multi-word wide value + AstWordSel(FileLine* fl, AstNode* fromp, AstNode* bitp) + :AstNodeSel(fl, fromp, bitp) { + width(VL_WORDSIZE,VL_WORDSIZE); // Always used on, and returns word entities + } + virtual ~AstWordSel() {} + virtual AstType type() const { return AstType::WORDSEL;} + virtual AstNode* clone() { return new AstWordSel(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& from, const V3Number& bit) { V3ERROR_NA; } + virtual string emitVerilog() { return "%k(%l[%r])"; } // Not %k, as usually it's a small constant rhsp + virtual string emitOperator() { V3ERROR_NA; return ""; } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + +struct AstSelExtract : public AstNodePreSel { + // Range extraction, gets replaced with AstSel + AstSelExtract(FileLine* fl, AstNode* fromp, AstNode* msbp, AstNode* lsbp) + : AstNodePreSel(fl, fromp, msbp, lsbp) {} + virtual ~AstSelExtract() {} + virtual AstType type() const { return AstType::SELEXTRACT;} + virtual AstNode* clone() { return new AstSelExtract(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstSelBit : public AstNodePreSel { + // Single bit range extraction, perhaps with non-constant selection or array selection + // Gets replaced during link with AstArraySel or AstSel + AstSelBit(FileLine* fl, AstNode* fromp, AstNode* bitp) + :AstNodePreSel(fl, fromp, bitp, NULL) { + width(1,1); + } + virtual ~AstSelBit() {} + virtual AstType type() const { return AstType::SELBIT;} + virtual AstNode* clone() { return new AstSelBit(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstSelPlus : public AstNodePreSel { + // +: range extraction, perhaps with non-constant selection + // Gets replaced during link with AstSel + AstSelPlus(FileLine* fl, AstNode* fromp, AstNode* bitp, AstNode* widthp) + :AstNodePreSel(fl, fromp, bitp, widthp) {} + virtual ~AstSelPlus() {} + virtual AstType type() const { return AstType::SELPLUS;} + virtual AstNode* clone() { return new AstSelPlus(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstSelMinus : public AstNodePreSel { + // -: range extraction, perhaps with non-constant selection + // Gets replaced during link with AstSel + AstSelMinus(FileLine* fl, AstNode* fromp, AstNode* bitp, AstNode* widthp) + :AstNodePreSel(fl, fromp, bitp, widthp) {} + virtual ~AstSelMinus() {} + virtual AstType type() const { return AstType::SELMINUS;} + virtual AstNode* clone() { return new AstSelMinus(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstSel : public AstNodeTriop { + // Multiple bit range extraction + // Parents: math|stmt + // Children: varref|arraysel, math, constant math + AstSel(FileLine* fl, AstNode* fromp, AstNode* lsbp, AstNode* widthp) + :AstNodeTriop(fl, fromp, lsbp, widthp) { + if (widthp->castConst()) width(widthp->castConst()->asInt(), widthp->castConst()->asInt()); + } + AstSel(FileLine* fl, AstNode* fromp, int lsbp, int bitwidth) + :AstNodeTriop(fl, fromp, + new AstConst(fl,lsbp), new AstConst(fl,bitwidth)) { + width(bitwidth,bitwidth); + } + virtual ~AstSel() {} + virtual AstType type() const { return AstType::SEL;} + virtual AstNode* clone() { return new AstSel(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& from, const V3Number& bit, const V3Number& width) { + out.opRange(from, bit.asInt()+width.asInt()-1, bit.asInt()); } + virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially + virtual string emitOperator() { V3ERROR_NA; return ""; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true;} virtual bool cleanRhs() {return true;} + virtual bool cleanThs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool sizeMattersThs() {return false;} + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode*) const { return true; } + virtual int instrCount() const { return widthInstrs()*(lsbp()->castConst()?3:10); } + AstNode* fromp() const { return op1p()->castNode(); } // op1 = Extracting what (NULL=TBD during parsing) + AstNode* lsbp() const { return op2p()->castNode(); } // op2 = Msb selection expression + AstNode* widthp() const { return op3p()->castNode(); } // op3 = Width + uint32_t lsbConst() const { return lsbp()->castConst()->asInt(); } + uint32_t widthConst() const { return widthp()->castConst()->asInt(); } + uint32_t msbConst() const { return lsbConst()+widthConst()-1; } +}; + +struct AstVar : public AstNode { + // A variable (in/out/wire/reg/param) inside a module +private: + string m_name; // Name of variable + AstVarType m_varType; // Type of variable + bool m_input:1; // Input or inout + bool m_output:1; // Output or inout + bool m_tristate:1; // Inout or triwire or trireg + bool m_primaryIO:1; // In/out to top level (or directly assigned from same) + bool m_sc:1; // SystemC variable + bool m_scClocked:1; // SystemC sc_clk<> needed + bool m_scSensitive:1;// SystemC sensitive() needed + bool m_sigPublic:1; // User C code accesses this signal + bool m_usedClock:1; // Signal used as a clock + bool m_usedParam:1; // Parameter is referenced (on link; later signals not setup) + bool m_funcLocal:1; // Local variable for a function + bool m_funcReturn:1; // Return variable for a function + bool m_attrClockEn:1;// User clock enable attribute + bool m_fileDescr:1; // File descriptor + bool m_isConst:1; // Table contains constant data + bool m_isStatic:1; // Static variable + bool m_trace:1; // Trace this variable + + void init() { + m_input=false; m_output=false; m_tristate=false; + m_primaryIO=false; + m_sc=false; m_scClocked=false; m_scSensitive=false; + m_usedClock=false; m_usedParam=false; + m_sigPublic=false; m_funcLocal=false; m_funcReturn=false; + m_attrClockEn=false; m_fileDescr=false; m_isConst=false; m_isStatic=false; + m_trace=false; + } +public: + AstVar(FileLine* fl, AstVarType type, const string& name) + :AstNode(fl) + , m_name(name) { + init(); + combineType(type); + width(msb()-lsb()+1,0); + } + AstVar(FileLine* fl, AstVarType type, const string& name, AstRange* rangep, AstRange* arrayp=NULL) + :AstNode(fl) + , m_name(name) { + init(); + combineType(type); setNOp1p(rangep); addNOp2p(arrayp); + width(msb()-lsb()+1,0); + } + AstVar(FileLine* fl, AstVarType type, const string& name, AstVar* examplep) + :AstNode(fl) + , m_name(name) { + init(); + combineType(type); + if (examplep->rangep()) { + setOp1p(new AstRange(fl, examplep->msb(), examplep->lsb())); + } + width(msb()-lsb()+1,0); + } + virtual ~AstVar() {} + virtual AstType type() const { return AstType::VAR;} + virtual AstNode* clone() { return new AstVar(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + virtual string name() const { return m_name; } // * = Var name + AstVarType varType() const { return m_varType; } // * = Type of variable + string cType() const; // Return C type: bool, uint32_t, uint64_t, etc. + void combineType(AstVarType type); + AstRange* rangep() const { return op1p()->castRange(); } // op1 = Range of variable + AstRange* arraysp() const { return op2p()->castRange(); } // op2 = Array(s) of variable + AstRange* arrayp(int dim) const; // op2 = Range for specific dimension # + AstNode* initp() const { return op3p()->castNode(); } // op3 = Initial value that never changes (static const) + void initp(AstNode* nodep) { setOp3p(nodep); } + bool hasSimpleInit() const { return (op3p() && !op3p()->castInitArray()); } + void rangep(AstRange* nodep) { setOp1p(nodep); } + void attrClockEn(bool flag) { m_attrClockEn = flag; } + void attrFileDescr(bool flag) { m_fileDescr = flag; } + void attrScClocked(bool flag) { m_scClocked = flag; } + void usedClock(bool flag) { m_usedClock = flag; } + void usedParam(bool flag) { m_usedParam = flag; } + void sigPublic(bool flag) { m_sigPublic = flag; } + void sc(bool flag) { m_sc = flag; } + void scSensitive(bool flag) { m_scSensitive = flag; } + void primaryIO(bool flag) { m_primaryIO = flag; } + void isConst(bool flag) { m_isConst = flag; } + void isStatic(bool flag) { m_isStatic = flag; } + void funcLocal(bool flag) { m_funcLocal = flag; } + void funcReturn(bool flag) { m_funcReturn = flag; } + void trace(bool flag) { m_trace=flag; } + // METHODS + void name(const string& name) { m_name = name; } + bool isInput() const { return m_input; } + bool isOutput() const { return m_output; } + bool isTristate() const { return (m_tristate); } + bool isPrimaryIO() const { return m_primaryIO; } + bool isPrimaryIn() const { return isPrimaryIO() && isInput(); } + bool isIO() const { return (m_input||m_output||m_tristate); } + bool isSignal() const { return (varType()==AstVarType::WIRE || varType()==AstVarType::IMPLICIT + || varType()==AstVarType::REG || varType()==AstVarType::INTEGER); } + bool isTemp() const { return (varType()==AstVarType::BLOCKTEMP || varType()==AstVarType::MODULETEMP + || varType()==AstVarType::STMTTEMP); } + bool isStatementTemp() const { return (varType()==AstVarType::STMTTEMP); } + bool isMovableToBlock() const { return (varType()==AstVarType::BLOCKTEMP || isFuncLocal()); } + bool isParam() const { return (varType()==AstVarType::LPARAM || varType()==AstVarType::GPARAM); } + bool isGParam() const { return (varType()==AstVarType::GPARAM); } + bool isGenVar() const { return (varType()==AstVarType::GENVAR); } + bool isInteger() const { return (varType() == AstVarType::INTEGER); } + bool isUsedClock() const { return m_usedClock; } + bool isUsedParam() const { return m_usedParam; } + bool isSc() const { return m_sc; } + bool isScQuad() const; + bool isScWide() const; + bool isScSensitive() const { return m_scSensitive; } + bool isSigPublic() const; + bool isTrace() const { return m_trace; } + bool isConst() const { return m_isConst; } + bool isStatic() const { return m_isStatic; } + bool isFuncLocal() const { return m_funcLocal; } + bool isFuncReturn() const { return m_funcReturn; } + bool attrClockEn() const { return m_attrClockEn; } + bool attrFileDescr() const { return m_fileDescr; } + bool attrScClocked() const { return m_scClocked; } + int widthAlignBytes() const; // Structure alignment 1,2,4 or 8 bytes (arrays affect this) + int widthTotalBytes() const; // Width in bytes rounding up 1,2,4,8,12,... + uint32_t msb() const { if (!rangep()) return 0; return rangep()->msbConst(); } + uint32_t lsb() const { if (!rangep()) return 0; return rangep()->lsbConst(); } + uint32_t arrayElements() const; // 1, or total multiplication of all dimensions + virtual string verilogKwd() const; + void propagateAttrFrom(AstVar* fromp) { + // This is getting connected to fromp; keep attributes + // Note the method below too + if (fromp->attrClockEn()) attrClockEn(true); + if (fromp->attrFileDescr()) attrFileDescr(true); + } + bool gateMultiInputOptimizable() const { + // Ok to gate optimize; must return false if propagateAttrFrom would do anything + return (!attrClockEn() && !isUsedClock()); + } + void combineType(AstVar* typevarp) { + // This is same as typevarp (for combining input & reg decls) + propagateAttrFrom(typevarp); + combineType(typevarp->varType()); + if (typevarp->isSigPublic()) sigPublic(true); + if (typevarp->attrScClocked()) attrScClocked(true); + } + void inlineAttrReset(const string& name) { + m_input=m_output=false; m_name = name; + if (varType()==AstVarType::INOUT) m_varType = AstVarType::TRIWIRE; + if (varType()==AstVarType::INPUT || varType()==AstVarType::OUTPUT) m_varType = AstVarType::WIRE; + } +}; + +struct AstDefParam : public AstNode { + // A defparam assignment + // Parents: MODULE + // Children: math +private: + string m_name; // Name of variable getting set + string m_path; // Dotted cellname to set parameter of +public: + AstDefParam(FileLine* fl, const string& path, const string& name, AstNode* rhsp) + : AstNode(fl) { + setOp1p(rhsp); + m_name = name; + m_path = path; + } + virtual ~AstDefParam() {} + virtual AstType type() const { return AstType::DEFPARAM;} + virtual string name() const { return m_name; } // * = Scope name + virtual AstNode* clone() { return new AstDefParam(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool cleanRhs() { return true; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode*) const { return true; } + AstNode* rhsp() const { return op1p()->castNode(); } // op1 = Assign from + string path() const { return m_path; } +}; + +struct AstScope : public AstNode { + // A particular usage of a cell + // Parents: MODULE + // Children: NODEBLOCK +private: + string m_name; // Name + AstScope* m_aboveScopep; // Scope above this one in the hierarchy (NULL if top) + AstCell* m_aboveCellp; // Cell above this in the hiearchy (NULL if top) + AstModule* m_modp; // Module scope corresponds to +public: + AstScope(FileLine* fl, AstModule* modp, const string& name, + AstScope* aboveScopep, AstCell* aboveCellp) + :AstNode(fl) + ,m_name(name) ,m_aboveScopep(aboveScopep) ,m_aboveCellp(aboveCellp), m_modp(modp) {} + virtual ~AstScope() {} + virtual AstType type() const { return AstType::SCOPE;} + virtual AstNode* clone() { return new AstScope(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void cloneRelink(); + virtual bool broken() const; + virtual string name() const { return m_name; } // * = Scope name + void name(const string& name) { m_name = name; } + string nameDotless() const; + string nameVlSym() const { return (((string)"VlSym->") + nameDotless()); } + AstModule* modp() const { return m_modp; } + void addVarp(AstNode* nodep) { addOp1p(nodep); } + AstNode* varsp() const { return op1p()->castNode(); } // op1 = AstVarScope's + void addActivep(AstNode* nodep) { addOp2p(nodep); } + AstNode* blocksp() const { return op2p()->castNode(); } // op2 = Block names + void addFinalClkp(AstNode* nodep) { addOp3p(nodep); } + AstNode* finalClksp() const { return op3p()->castNode(); } // op3 = Final assigns for clock correction + AstScope* aboveScopep() const { return m_aboveScopep; } + AstCell* aboveCellp() const { return m_aboveCellp; } + bool isTop() const { return aboveScopep()==NULL; } // At top of hierarchy +}; + +struct AstTopScope : public AstNode { + // In the top level netlist, a complete scope tree + // There may be two of these, when we support "rare" and "usual" splitting + // Parents: topMODULE + // Children: SCOPEs +public: + AstTopScope(FileLine* fl, AstScope* ascopep) + :AstNode(fl) + {addNOp2p(ascopep);} + virtual ~AstTopScope() {} + virtual AstType type() const { return AstType::TOPSCOPE;} + virtual AstNode* clone() { return new AstTopScope(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstNode* stmtsp() const { return op1p()->castNode(); } + void addStmtsp(AstNode* nodep) { addOp1p(nodep); } + AstNode* scopep() const { return op2p()->castNode(); } // op1 = AstVarScope's +}; + +struct AstVarScope : public AstNode { + // A particular scoped usage of a variable + // That is, as a module is used under multiple cells, we get a different varscope for each var in the module + // Parents: MODULE + // Children: none +private: + AstScope* m_scopep; // Scope variable is underneath + AstVar* m_varp; // [AfterLink] Pointer to variable itself + bool m_circular:1; // Used in circular ordering dependency, need change detect +public: + AstVarScope(FileLine* fl, AstScope* scopep, AstVar* varp) + :AstNode(fl) + , m_scopep(scopep), m_varp(varp) { + m_circular = false; + widthSignedFrom(varp); + } + virtual ~AstVarScope() {} + virtual AstType type() const { return AstType::VARSCOPE;} + virtual AstNode* clone() { return new AstVarScope(*this);} + virtual void cloneRelink() { if (m_varp && m_varp->clonep()) { + m_varp = m_varp->clonep()->castVar(); + UASSERT(m_scopep->clonep(), "No clone cross link: "<clonep()->castScope(); + }} + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool broken() const { return ( (m_varp && !m_varp->brokeExists()) + || (m_scopep && !m_scopep->brokeExists())); } + virtual string name() const {return scopep()->name()+"->"+varp()->name();} // * = Var name + virtual void dump(ostream& str); + AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable + AstScope* scopep() const { return m_scopep; } // Pointer to scope it's under + AstNode* valuep() const { return op1p(); } // op1 = Calculation of value of variable, NULL=complicated + void valuep(AstNode* valuep) { addOp1p(valuep); } + bool isCircular() const { return m_circular; } + void circular(bool flag) { m_circular = flag; } +}; + +struct AstVarRef : public AstNodeVarRef { + // A reference to a variable (lvalue or rvalue) +private: +public: + AstVarRef(FileLine* fl, const string& name, bool lvalue) + :AstNodeVarRef(fl, name, NULL, lvalue) {} + AstVarRef(FileLine* fl, AstVar* varp, bool lvalue) // This form only allowed post-link + :AstNodeVarRef(fl, varp->name(), varp, lvalue) {} // because output/wire compression may lead to deletion of AstVar's + AstVarRef(FileLine* fl, AstVarScope* varscp, bool lvalue) // This form only allowed post-link + :AstNodeVarRef(fl, varscp->varp()->name(), varscp->varp(), lvalue) { // because output/wire compression may lead to deletion of AstVar's + varScopep(varscp); + } + virtual ~AstVarRef() {} + virtual AstType type() const { return AstType::VARREF;} + virtual AstNode* clone() { return new AstVarRef(*this);} + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + virtual V3Hash sameHash() const { return V3Hash(V3Hash(varp()->name()),V3Hash(hiername())); } + virtual bool same(AstNode* samep) const { + if (varScopep()) return varScopep()==samep->castVarRef()->varScopep(); + else return (hiername()==samep->castVarRef()->hiername() + && varp()->name()==samep->castVarRef()->varp()->name()); } + virtual int instrCount() const { return widthInstrs()*(lvalue()?1:instrCountLd()); } + virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially + virtual string emitOperator() { V3ERROR_NA; return ""; } + virtual bool cleanOut() { return true; } +}; + +struct AstVarXRef : public AstNodeVarRef { + // A VarRef to something in another module before AstScope. + // Includes pin on a cell, as part of a ASSIGN statement to connect I/Os until AstScope +private: + string m_dotted; // Scope name to connected to + string m_inlinedDots; // Dotted hiearchy flattened out +public: + AstVarXRef(FileLine* fl, const string& name, const string& dotted, bool lvalue) + :AstNodeVarRef(fl, name, NULL, lvalue) + , m_dotted(dotted) { } + AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, bool lvalue) + :AstNodeVarRef(fl, varp->name(), varp, lvalue) + , m_dotted(dotted) { + } + virtual ~AstVarXRef() {} + virtual AstType type() const { return AstType::VARXREF;} + virtual AstNode* clone() { return new AstVarXRef(*this);} + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + string dotted() const { return m_dotted; } + string prettyDotted() const { return prettyName(dotted()); } + string inlinedDots() const { return m_inlinedDots; } + void inlinedDots(const string& flag) { m_inlinedDots = flag; } + virtual string emitVerilog() { V3ERROR_NA; return ""; } + virtual string emitOperator() { V3ERROR_NA; return ""; } + virtual bool cleanOut() { return true; } + virtual int instrCount() const { return widthInstrs(); } + virtual V3Hash sameHash() const { return V3Hash(V3Hash(varp()),V3Hash(dotted())); } + virtual bool same(AstNode* samep) const { + return (hiername()==samep->castVarXRef()->hiername() + && varp()==samep->castVarXRef()->varp() + && name()==samep->castVarXRef()->name() + && dotted()==samep->castVarXRef()->dotted()); } +}; + +struct AstPin : public AstNode { + // A pin on a cell +private: + int m_pinNum; // Pin number + string m_name; // Pin name, or "" for number based interconnect + AstVar* m_modVarp; // Input/output this pin connects to on submodule. +public: + AstPin(FileLine* fl, int pinNum, const string& name, AstNode* exprp) + :AstNode(fl) + ,m_name(name) { + m_pinNum = pinNum; + m_modVarp = NULL; + setNOp1p(exprp); } + virtual ~AstPin() {} + virtual AstType type() const { return AstType::PIN;} + virtual AstNode* clone() { return new AstPin(*this);} + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + virtual bool broken() const { return (m_modVarp && !m_modVarp->brokeExists()); } + virtual string name() const { return m_name; } // * = Pin name, ""=go by number + void name(const string& name) { m_name = name; } + int pinNum() const { return m_pinNum; } + void exprp(AstNode* nodep) { addOp1p(nodep); } + AstNode* exprp() const { return op1p()->castNode(); } // op1 = Expression connected to pin + AstVar* modVarp() const { return m_modVarp; } // [After Link] Pointer to variable + void modVarp(AstVar* varp) { m_modVarp=varp; } +}; + +struct AstModule : public AstNode { + // A module declaration +private: + string m_name; // Name of the module + string m_origName; // Name of the module, ignoring name() changes, for dot lookup + bool m_globalSyms:1; // References global symbol table + bool m_modPublic:1; // Module has public references + bool m_modTrace:1; // Tracing this module + bool m_inLibrary:1; // From a library, no error if not used, never top level + int m_level; // 1=top module, 2=cell off top module, ... + int m_varNum; // Incrementing variable number + AstVar* m_clkReqVarp; // Clock request variable +public: + AstModule(FileLine* fl, const string& name) + : AstNode (fl) + ,m_name(name), m_origName(name), m_globalSyms(false), m_modPublic(false) + ,m_modTrace(false), m_inLibrary(false) + ,m_level(0), m_varNum(0), m_clkReqVarp(NULL) { } + virtual ~AstModule() {} + virtual AstType type() const { return AstType::MODULE;} + virtual AstNode* clone() { return new AstModule(*this);} + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + virtual bool broken() const { return (m_clkReqVarp && !m_clkReqVarp->brokeExists()); } + virtual string name() const { return m_name; } + AstNode* stmtsp() const { return op2p()->castNode(); } // op2 = List of statements + AstActive* activesp() const { return op3p()->castActive(); } // op3 = List of i/sblocks + // METHODS + void addInlinesp(AstNode* nodep) { addOp1p(nodep); } + void addStmtp(AstNode* nodep) { addOp2p(nodep); } + void addActivep(AstNode* nodep) { addOp3p(nodep); } + // ACCESSORS + void name(const string& name) { m_name = name; } + string origName() const { return m_origName; } + bool inLibrary() const { return m_inLibrary; } + void inLibrary(bool flag) { m_inLibrary = flag; } + void level(int level) { m_level = level; } + int level() const { return m_level; } + bool isTop() const { return level()==1; } + int varNumGetInc() { return ++m_varNum; } + AstVar* clkReqVarp() const { return m_clkReqVarp; } + void clkReqVarp(AstVar* varp) { m_clkReqVarp = varp; } + void globalSyms(bool flag) { m_globalSyms = flag; } + bool globalSyms() const { return m_globalSyms; } + void modPublic(bool flag) { m_modPublic = flag; } + bool modPublic() const { return m_modPublic; } + void modTrace(bool flag) { m_modTrace = flag; } + bool modTrace() const { return m_modTrace; } +}; + +struct AstCell : public AstNode { + // A instantiation cell +private: + string m_name; // Cell name + string m_origName; // Original name before dot addition + string m_modName; // Module the cell instances + AstModule* m_modp; // [AfterLink] Pointer to module instanced +public: + AstCell(FileLine* fl, const string& instName, const string& modName, + AstPin* pinsp, AstPin* paramsp, AstRange* rangep) + : AstNode(fl) + , m_name(instName), m_origName(instName), m_modName(modName), m_modp(NULL) { + addNOp1p(pinsp); addNOp2p(paramsp); setNOp3p(rangep); } + virtual ~AstCell() {} + virtual AstType type() const { return AstType::CELL;} + virtual AstNode* clone() { return new AstCell(*this);} + // No cloneRelink, we presume cloneee's want the same module linkages + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + virtual bool broken() const { return (m_modp && !m_modp->brokeExists()); } + // ACCESSORS + virtual string name() const { return m_name; } // * = Cell name + string origName() const { return m_origName; } // * = Original name + string modName() const { return m_modName; } // * = Instance name + AstPin* pinsp() const { return op1p()->castPin(); } // op1 = List of cell ports + AstPin* paramsp() const { return op2p()->castPin(); } // op2 = List of parameter #(##) values + AstRange* rangep() const { return op3p()->castRange(); } // op3 = Range of arrayed instants (NULL=not ranged) + AstModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated + void addPinsp(AstPin* pinp) { addOp1p(pinp); } + void addParamsp(AstPin* pinp) { addOp2p(pinp); } + void modp(AstModule* modp) { m_modp = modp; } + void modName(const string& name) { m_modName = name; } + void name(const string& name) { m_name = name; } +}; + +struct AstCellInline : public AstNode { + // A instantiation cell that was removed by inlining + // For communication between V3Inline and V3LinkDot only + // Children: When 2 levels inlined, other CellInline under this +private: + string m_name; // Cell name, possibly {a}__DOT__{b}... + string m_origModName; // Original name of the module, ignoring name() changes, for dot lookup +public: + AstCellInline(FileLine* fl, const string& name, const string& origModName) + : AstNode(fl) + , m_name(name), m_origModName(origModName) {} + virtual ~AstCellInline() {} + virtual AstType type() const { return AstType::CELLINLINE;} + virtual AstNode* clone() { return new AstCellInline(*this);} + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + // ACCESSORS + virtual string name() const { return m_name; } // * = Cell name + string origModName() const { return m_origModName; } // * = modp()->origName() before inlining + void name(const string& name) { m_name = name; } +}; + +struct AstPort : public AstNode { + // A port (in/out/inout) on a module +private: + int m_pinNum; // Pin number + string m_name; // Name of pin +public: + AstPort(FileLine* fl, int pinnum, const string& name) + :AstNode(fl) + ,m_pinNum(pinnum) ,m_name(name) {} + virtual ~AstPort() {} + virtual AstType type() const { return AstType::PORT;} + virtual AstNode* clone() { return new AstPort(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string name() const { return m_name; } // * = Port name + int pinNum() const { return m_pinNum; } // * = Pin number, for order based instantiation + AstNode* exprp() const { return op1p()->castNode(); } // op1 = Expression connected to port +}; + +//###################################################################### + +struct AstBegin : public AstNode { + // A Begin/end named block, only exists shortly after parsing until linking + // Parents: statement + // Children: statements +private: + string m_name; // Name of block +public: + // Node that simply puts name into the output stream + AstBegin(FileLine* fileline, const string& name, AstNode* stmtsp) + : AstNode(fileline) + , m_name(name) { + addNOp1p(stmtsp); + } + virtual ~AstBegin() {} + virtual AstType type() const { return AstType::BEGIN;} + virtual AstNode* clone() { return new AstBegin(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string name() const { return m_name; } // * = Block name + void name(const string& flag) { m_name=flag; } + // op1 = Statements + AstNode* stmtsp() const { return op1p()->castNode(); } // op1 = List of statements + void addStmtp(AstNode* nodep) { addOp1p(nodep); } +}; + +struct AstGenerate : public AstNode { + // A Generate/end block + // Parents: MODULE + // Children: modItems + AstGenerate(FileLine* fileline, AstNode* stmtsp) + : AstNode(fileline) { + addNOp1p(stmtsp); + } + virtual ~AstGenerate() {} + virtual AstType type() const { return AstType::GENERATE;} + virtual AstNode* clone() { return new AstGenerate(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + // op1 = Statements + AstNode* stmtsp() const { return op1p()->castNode(); } // op1 = List of statements + void addStmtp(AstNode* nodep) { addOp1p(nodep); } +}; + +//###################################################################### + +struct AstTask : public AstNodeFTask { + // A task inside a module + AstTask(FileLine* fl, const string& name, AstNode* stmtp) + :AstNodeFTask(fl, name, stmtp) {} + virtual ~AstTask() {} + virtual AstType type() const { return AstType::TASK;} + virtual AstNode* clone() { return new AstTask(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstFunc : public AstNodeFTask { + // A function inside a module + AstFunc(FileLine* fl, const string& name, AstNode* stmtp, AstNode* fvarsp) + :AstNodeFTask(fl, name, stmtp) { + addNOp1p(fvarsp); + } + virtual ~AstFunc() {} + virtual AstType type() const { return AstType::FUNC;} + virtual AstNode* clone() { return new AstFunc(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + // op1 = Range output variable (functions only) + AstNode* fvarp() const { return op1p()->castNode(); } + void addFvarp(AstNode* nodep) { addOp1p(nodep); } +}; + +struct AstTaskRef : public AstNodeFTaskRef { + // A reference to a task + AstTaskRef(FileLine* fl, const string& name, const string& dotted, AstNode* pinsp) + :AstNodeFTaskRef(fl, name, dotted, pinsp) {} + virtual ~AstTaskRef() {} + virtual AstType type() const { return AstType::TASKREF;} + virtual AstNode* clone() { return new AstTaskRef(*this);} + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstFuncRef : public AstNodeFTaskRef { + // A reference to a function + AstFuncRef(FileLine* fl, const string& name, const string& dotted, AstNode* pinsp) + :AstNodeFTaskRef(fl, name, dotted, pinsp) {} + virtual ~AstFuncRef() {} + virtual AstType type() const { return AstType::FUNCREF;} + virtual AstNode* clone() { return new AstFuncRef(*this);} + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +//###################################################################### + +struct AstSenItem : public AstNode { + // Parents: SENTREE + // Children: (optional) VARREF +private: + AstEdgeType m_edgeType; // Edge type +public: + class Combo {}; // for creator type-overload selection + class Initial {}; // for creator type-overload selection + class Settle {}; // for creator type-overload selection + class Never {}; // for creator type-overload selection + AstSenItem(FileLine* fl, AstEdgeType edgeType, AstVarRef* varrefp) + : AstNode(fl) { + m_edgeType = edgeType; + setOp1p(varrefp); + } + AstSenItem(FileLine* fl, Combo) + : AstNode(fl) { + m_edgeType = AstEdgeType::COMBO; + } + AstSenItem(FileLine* fl, Initial) + : AstNode(fl) { + m_edgeType = AstEdgeType::INITIAL; + } + AstSenItem(FileLine* fl, Settle) + : AstNode(fl) { + m_edgeType = AstEdgeType::SETTLE; + } + AstSenItem(FileLine* fl, Never) + : AstNode(fl) { + m_edgeType = AstEdgeType::NEVER; + } + virtual ~AstSenItem() {} + virtual AstType type() const { return AstType::SENITEM;} + virtual AstNode* clone() { return new AstSenItem(*this); } + AstSenItem* cloneTree(bool cloneNextLink) { return AstNode::cloneTree(cloneNextLink)->castSenItem(); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + virtual V3Hash sameHash() const { return V3Hash(V3Hash(edgeType())); } + virtual bool same(AstNode* samep) const { + return edgeType()==samep->castSenItem()->edgeType(); } + AstEdgeType edgeType() const { return m_edgeType; } // * = Posedge/negedge + void edgeType(AstEdgeType type) { m_edgeType=type; editCountInc(); }// * = Posedge/negedge + AstNode* sensp() const { return op1p(); } // op1 = Signal sensitized + AstVarRef* varrefp() const { return op1p()->castVarRef(); } // op1 = Signal sensitized + // + bool isClocked() const { return edgeType().clockedStmt(); } + bool isCombo() const { return edgeType()==AstEdgeType::COMBO; } + bool isInitial() const { return edgeType()==AstEdgeType::INITIAL; } + bool isSettle() const { return edgeType()==AstEdgeType::SETTLE; } + bool isNever() const { return edgeType()==AstEdgeType::NEVER; } + bool hasVar() const { return !(isCombo()||isInitial()||isSettle()||isNever()); } +}; + +struct AstSenTree : public AstNode { + // A list of senitems + // Parents: MODULE | SBLOCK + // Children: SENITEM list +private: + bool m_multi; // Created from combo logic by ORing multiple clock domains +public: + AstSenTree(FileLine* fl, AstSenItem* sensesp) + : AstNode(fl), m_multi(false) { + addNOp1p(sensesp); + } + virtual ~AstSenTree() {} + virtual AstType type() const { return AstType::SENTREE;} + virtual AstNode* clone() { return new AstSenTree(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + bool isMulti() const { return m_multi; } + AstSenItem* sensesp() const { return op1p()->castSenItem(); } // op1 = Sensitivity list + void addSensesp(AstSenItem* nodep) { addOp1p(nodep); } + void multi(bool flag) { m_multi = true; } + // METHODS + void sortSenses(); // Sort senitems in standard way + bool hasClocked(); // Includes a clocked statement + bool hasSettle(); // Includes a SETTLE SenItem + bool hasInitial(); // Includes a INITIAL SenItem + bool hasCombo(); // Includes a COMBO SenItem +}; + +struct AstAlways : public AstNode { + AstAlways(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp) + : AstNode(fl) { + addNOp1p(sensesp); addNOp2p(bodysp); + } + virtual ~AstAlways() {} + virtual AstType type() const { return AstType::ALWAYS;} + virtual AstNode* clone() { return new AstAlways(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + // + AstSenTree* sensesp() const { return op1p()->castSenTree(); } // op1 = Sensitivity list + AstNode* bodysp() const { return op2p()->castNode(); } // op2 = Statements to evaluate + void addStmtp(AstNode* nodep) { addOp2p(nodep); } + // Special accessors + bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); } +}; + +struct AstAlwaysPost : public AstNode { + // Like always but post assignments for memory assignment IFs + AstAlwaysPost(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp) + : AstNode(fl) { + addNOp1p(sensesp); addNOp2p(bodysp); + } + virtual ~AstAlwaysPost() {} + virtual AstType type() const { return AstType::ALWAYSPOST;} + virtual AstNode* clone() { return new AstAlwaysPost(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + // + AstNode* bodysp() const { return op2p()->castNode(); } // op2 = Statements to evaluate + void addBodysp(AstNode* newp) { addOp2p(newp); } +}; + +struct AstAssign : public AstNodeAssign { + AstAssign(FileLine* fileline, AstNode* lhsp, AstNode* rhsp) + : AstNodeAssign(fileline, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); + } + virtual ~AstAssign() {} + virtual AstType type() const { return AstType::ASSIGN;} + virtual AstNode* clone() { return new AstAssign(*this); } + virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssign(this->fileline(), lhsp, rhsp); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string verilogKwd() const { return "="; }; +}; + +struct AstAssignAlias : public AstNodeAssign { + // Like AstAssignW, but a true bidirect interconnection alias + // If both sides are wires, there's no LHS vs RHS, +public: + AstAssignAlias(FileLine* fileline, AstVarRef* lhsp, AstVarRef* rhsp) + : AstNodeAssign(fileline, lhsp, rhsp) {} + virtual ~AstAssignAlias() {} + virtual AstType type() const { return AstType::ASSIGNALIAS;} + virtual AstNode* clone() { return new AstAssignAlias(*this); } + virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { V3ERROR_NA; return NULL; } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstAssignDly : public AstNodeAssign { + AstAssignDly(FileLine* fileline, AstNode* lhsp, AstNode* rhsp) + : AstNodeAssign(fileline, lhsp, rhsp) {} + virtual ~AstAssignDly() {} + virtual AstType type() const { return AstType::ASSIGNDLY;} + virtual AstNode* clone() { return new AstAssignDly(*this); } + virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssignDly(this->fileline(), lhsp, rhsp); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool isGateOptimizable() const { return false; } + virtual string verilogKwd() const { return "<="; }; +}; + +struct AstAssignW : public AstNodeAssign { + // Like assign, but wire/assign's in verilog, the only setting of the specified variable +private: + bool m_allowImplicit; // Output can be a implicit wire +public: + AstAssignW(FileLine* fileline, AstNode* lhsp, AstNode* rhsp) + : AstNodeAssign(fileline, lhsp, rhsp) { + m_allowImplicit = false; + } + virtual ~AstAssignW() {} + virtual AstType type() const { return AstType::ASSIGNW;} + virtual AstNode* clone() { return new AstAssignW(*this); } + virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssignW(this->fileline(), lhsp, rhsp); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + bool allowImplicit() const { return m_allowImplicit; } + void allowImplicit(bool flag) { m_allowImplicit = flag; } +}; + +struct AstAssignPre : public AstNodeAssign { + // Like Assign, but predelayed assignment requiring special order handling + AstAssignPre(FileLine* fileline, AstNode* lhsp, AstNode* rhsp) + : AstNodeAssign(fileline, lhsp, rhsp) {} + virtual ~AstAssignPre() {} + virtual AstType type() const { return AstType::ASSIGNPRE;} + virtual AstNode* clone() { return new AstAssignPre(*this); } + virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssignPre(this->fileline(), lhsp, rhsp); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstAssignPost : public AstNodeAssign { + // Like Assign, but predelayed assignment requiring special order handling + AstAssignPost(FileLine* fileline, AstNode* lhsp, AstNode* rhsp) + : AstNodeAssign(fileline, lhsp, rhsp) {} + virtual ~AstAssignPost() {} + virtual AstType type() const { return AstType::ASSIGNPOST;} + virtual AstNode* clone() { return new AstAssignPost(*this); } + virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssignPost(this->fileline(), lhsp, rhsp); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstComment : public AstNodeStmt { + // Some comment to put into the output stream + // Parents: {statement list} + // Children: none +private: + string m_name; // Name of variable +public: + AstComment(FileLine* fl, const string& name) + : AstNodeStmt(fl) + , m_name(name) {} + virtual ~AstComment() {} + virtual AstType type() const { return AstType::COMMENT;} + virtual AstNode* clone() { return new AstComment(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string name() const { return m_name; } // * = Var name + virtual V3Hash sameHash() const { return V3Hash(); } // Ignore name in comments + virtual bool same(AstNode* samep) const { return true; } // Ignore name in comments +}; + +struct AstCond : public AstNodeCond { + // Conditional ?: statement + // Parents: MATH + // Children: MATH + AstCond(FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p) + : AstNodeCond(fl, condp, expr1p, expr2p) {} + virtual ~AstCond() {} + virtual AstType type() const { return AstType::COND;} + virtual AstNode* clone() { return new AstCond(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstCondBound : public AstNodeCond { + // Conditional ?: statement, specially made for saftey checking of array bounds + // Parents: MATH + // Children: MATH + AstCondBound(FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p) + : AstNodeCond(fl, condp, expr1p, expr2p) {} + virtual ~AstCondBound() {} + virtual AstType type() const { return AstType::CONDBOUND;} + virtual AstNode* clone() { return new AstCondBound(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstCoverDecl : public AstNodeStmt { + // Coverage analysis point declaration + // Parents: {statement list} + // Children: none +private: + string m_typeText; + string m_text; + string m_hier; + int m_column; +public: + AstCoverDecl(FileLine* fl, int column, const string& type, const string& comment) + : AstNodeStmt(fl) { + m_text = comment; m_typeText = type; m_column = column; + } + virtual ~AstCoverDecl() {} + virtual AstType type() const { return AstType::COVERDECL;} + virtual AstNode* clone() { return new AstCoverDecl(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + virtual int instrCount() const { return 1+2*instrCountLd(); } + int column() const { return m_column; } + const string& comment() const { return m_text; } // text to insert in code + const string& typeText() const { return m_typeText; } + const string& hier() const { return m_hier; } + void hier(const string& flag) { m_hier=flag; } + void comment(const string& flag) { m_text=flag; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { + return (fileline() == samep->castCoverDecl()->fileline() + && hier()==samep->castCoverDecl()->hier() + && comment()==samep->castCoverDecl()->comment() + && column()==samep->castCoverDecl()->column()); } + virtual bool isPredictOptimizable() const { return false; } +}; + +struct AstCoverInc : public AstNodeStmt { + // Coverage analysis point; increment coverage count + // Parents: {statement list} + // Children: none +private: + AstCoverDecl* m_declp; // [After V3Coverage] Pointer to declaration +public: + AstCoverInc(FileLine* fl, AstCoverDecl* declp) + : AstNodeStmt(fl) { + m_declp = declp; + } + virtual ~AstCoverInc() {} + virtual AstType type() const { return AstType::COVERINC;} + virtual AstNode* clone() { return new AstCoverInc(*this); } + virtual bool broken() const { return !declp()->brokeExists(); } + virtual void cloneRelink() { if (m_declp->clonep()) m_declp = m_declp->clonep()->castCoverDecl(); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + virtual int instrCount() const { return 1+2*instrCountLd(); } + virtual V3Hash sameHash() const { return V3Hash(declp()); } + virtual bool same(AstNode* samep) const { + return declp()==samep->castCoverInc()->declp(); } + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual bool isOutputter() const { return true; } + // but isSplittable() true + AstCoverDecl* declp() const { return m_declp; } // Where defined +}; + +struct AstGenCase : public AstNodeCase { + // Generate Case statement + // Parents: {statement list} + // exprp Children: MATHs + // casesp Children: CASEITEMs +public: + AstGenCase(FileLine* fileline, AstNode* exprp, AstNode* casesp) + : AstNodeCase(fileline, exprp, casesp) { + } + virtual ~AstGenCase() {} + virtual AstType type() const { return AstType::GENCASE;} + virtual AstNode* clone() { return new AstGenCase(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstCase : public AstNodeCase { + // Case statement + // Parents: {statement list} + // exprp Children: MATHs + // casesp Children: CASEITEMs +private: + bool m_casex; // True if casex instead of normal case. + bool m_fullPragma; // Synthesis full_case + bool m_parallelPragma; // Synthesis parallel_case +public: + AstCase(FileLine* fileline, bool casex, AstNode* exprp, AstNode* casesp) + : AstNodeCase(fileline, exprp, casesp) { + m_casex=casex; + m_fullPragma=false; m_parallelPragma=false; + } + virtual ~AstCase() {} + virtual AstType type() const { return AstType::CASE;} + virtual AstNode* clone() { return new AstCase(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string verilogKwd() const { return casex()?"casex":"case"; } + bool casex() const { return m_casex; } + bool fullPragma() const { return m_fullPragma; } + bool parallelPragma() const { return m_parallelPragma; } + void fullPragma(bool flag) { m_fullPragma=flag; } + void parallelPragma(bool flag) { m_parallelPragma=flag; } +}; + +struct AstCaseItem : public AstNode { + // Single item of a case statement + // Parents: CASE + // condsp Children: MATH (Null condition used for default block) + // bodysp Children: Statements +private: + bool m_ignoreOverlap; // Default created by assertions; ignore overlaps +public: + AstCaseItem(FileLine* fileline, AstNode* condsp, AstNode* bodysp) + : AstNode(fileline) { + addNOp1p(condsp); addNOp2p(bodysp); + m_ignoreOverlap = false; + } + virtual ~AstCaseItem() {} + virtual AstType type() const { return AstType::CASEITEM;} + virtual AstNode* clone() { return new AstCaseItem(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } + AstNode* condsp() const { return op1p()->castNode(); } // op1= list of possible matching expressions + AstNode* bodysp() const { return op2p()->castNode(); } // op2= what to do + void addBodysp(AstNode* newp) { addOp2p(newp); } + bool isDefault() const { return condsp()==NULL; } + bool ignoreOverlap() const { return m_ignoreOverlap; } + void ignoreOverlap(bool flag) { m_ignoreOverlap = flag; } +}; + +struct AstDisplay : public AstNodePli { +private: + char m_newline; +public: + AstDisplay(FileLine* fileline, char newln, const string& text, AstNodeVarRef* filep, AstNode* exprsp) + : AstNodePli (fileline, text, exprsp) { + setNOp2p(filep); + m_newline = newln; + } + virtual ~AstDisplay() {} + virtual AstType type() const { return AstType::DISPLAY;} + virtual AstNode* clone() { return new AstDisplay(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string verilogKwd() const { return (filep() ? ((newline() == '\n')?"$fdisplay":"$fwrite") + : ((newline() == '\n')?"$display":"$write")); }; + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual bool isSplittable() const { return false; } // SPECIAL: $display has 'visual' ordering + virtual bool isOutputter() const { return true; } // SPECIAL: $display makes output + virtual bool isUnlikely() const { return true; } + virtual V3Hash sameHash() const { return V3Hash(text()); } + virtual bool same(AstNode* samep) const { + return newline()==samep->castDisplay()->newline() + && text()==samep->castDisplay()->text(); } + // op1 used by AstNodePli + char newline() const { return m_newline; } // * = Add a newline for $display + AstNodeVarRef* filep() const { return op2p()->castVarRef(); } + void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); } + AstNode* scopeAttrp() const { return op3p(); } + AstText* scopeTextp() const { return op3p()->castText(); } + void scopeAttrp(AstNode* nodep) { addOp3p(nodep); } +}; + +struct AstFClose : public AstNodeStmt { + AstFClose(FileLine* fileline, AstNodeVarRef* filep) + : AstNodeStmt (fileline) { + setNOp2p(filep); + } + virtual ~AstFClose() {} + virtual AstType type() const { return AstType::FCLOSE;} + virtual AstNode* clone() { return new AstFClose(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string verilogKwd() const { return "$fclose"; }; + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual bool isSplittable() const { return false; } + virtual bool isOutputter() const { return true; } + virtual bool isUnlikely() const { return true; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } + AstNodeVarRef* filep() const { return op2p()->castVarRef(); } + void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); } +}; + +struct AstFOpen : public AstNodeStmt { + AstFOpen(FileLine* fileline, AstNode* filep, AstNode* filenamep, AstNode* modep) + : AstNodeStmt (fileline) { + setOp1p(filep); + setOp2p(filenamep); + setOp3p(modep); + } + virtual ~AstFOpen() {} + virtual AstType type() const { return AstType::FOPEN;} + virtual AstNode* clone() { return new AstFOpen(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string verilogKwd() const { return "$fclose"; }; + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual bool isSplittable() const { return false; } + virtual bool isOutputter() const { return true; } + virtual bool isUnlikely() const { return true; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } + AstNodeVarRef* filep() const { return op1p()->castVarRef(); } + AstNode* filenamep() const { return op2p(); } + AstNode* modep() const { return op3p(); } +}; + +struct AstGenFor : public AstNodeFor { + AstGenFor(FileLine* fileline, AstNode* initsp, AstNode* condp, + AstNode* assignsp, AstNode* bodysp) + : AstNodeFor(fileline, initsp, condp, assignsp, bodysp) { + } + virtual ~AstGenFor() {} + virtual AstType type() const { return AstType::GENFOR;} + virtual AstNode* clone() { return new AstGenFor(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstFor : public AstNodeFor { + AstFor(FileLine* fileline, AstNode* initsp, AstNode* condp, + AstNode* assignsp, AstNode* bodysp) + : AstNodeFor(fileline, initsp, condp, assignsp, bodysp) { + } + virtual ~AstFor() {} + virtual AstType type() const { return AstType::FOR;} + virtual AstNode* clone() { return new AstFor(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstWhile : public AstNodeStmt { + AstWhile(FileLine* fileline, AstNode* condp, AstNode* bodysp) + : AstNodeStmt(fileline) { + setOp2p(condp); addNOp3p(bodysp); + } + virtual ~AstWhile() {} + virtual AstType type() const { return AstType::WHILE;} + virtual AstNode* clone() { return new AstWhile(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstNode* precondsp() const { return op1p()->castNode(); } // op1= prepare statements for condition (exec every loop) + AstNode* condp() const { return op2p()->castNode(); } // op2= condition to continue + AstNode* bodysp() const { return op3p()->castNode(); } // op3= body of loop + void addPrecondsp(AstNode* newp) { addOp1p(newp); } + void addBodysp(AstNode* newp) { addOp3p(newp); } + 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 AstGenIf : public AstNodeIf { + AstGenIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp) + : AstNodeIf(fileline, condp, ifsp, elsesp) { + } + virtual ~AstGenIf() {} + virtual AstType type() const { return AstType::GENIF;} + virtual AstNode* clone() { return new AstGenIf(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstIf : public AstNodeIf { + AstIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp) + : AstNodeIf(fileline, condp, ifsp, elsesp) { + } + virtual ~AstIf() {} + virtual AstType type() const { return AstType::IF;} + virtual AstNode* clone() { return new AstIf(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstUntilStable : public AstNodeStmt { + // Quasi-while loop until given signals are stable + // Parents: CFUNC (generally) + // Children: VARREF, statements + AstUntilStable(FileLine* fileline, AstVarRef* stablesp, AstNode* bodysp) + : AstNodeStmt(fileline) { + addNOp2p(stablesp); addNOp3p(bodysp); + } + virtual ~AstUntilStable() {} + virtual AstType type() const { return AstType::UNTILSTABLE;} + virtual AstNode* clone() { return new AstUntilStable(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstVarRef* stablesp() const { return op2p()->castVarRef(); } // op2= list of variables that must become stable + AstNode* bodysp() const { return op3p()->castNode(); } // op3= body of loop + void addStablesp(AstVarRef* newp) { addOp2p(newp); } + void addBodysp(AstNode* newp) { addOp3p(newp); } + virtual bool isGateOptimizable() const { return false; } // Not relevant + virtual bool isPredictOptimizable() const { return false; } // Not relevant + virtual int instrCount() const { return instrCountBranch(); } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + +struct AstChangeXor : public AstNodeBiComAsv { + // A comparison to determine change detection, common & must be fast. + // Returns 32-bit or 64-bit value where 0 indicates no change. + // Parents: OR or LOGOR + // Children: VARREF + AstChangeXor(FileLine* fl, AstNode* lhsp, AstNode* rhsp) + : AstNodeBiComAsv(fl, lhsp, rhsp) { + width(32,32); } + virtual ~AstChangeXor() {} + virtual AstType type() const { return AstType::CHANGEXOR;} + virtual AstNode* clone() { return new AstChangeXor(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opChangeXor(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l ^ %r)"; } + virtual string emitOperator() { return "VL_CHANGEXOR"; } + virtual string emitSimpleOperator() { return "^"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} // Lclean && Rclean + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return widthInstrs(); } +}; + +struct AstChangeDet : public AstNodeStmt { + // A comparison to determine change detection, common & must be fast. +private: + bool m_clockReq; // Type of detection +public: + // Null lhs+rhs used to indicate change needed with no spec vars + AstChangeDet(FileLine* fl, AstNode* lhsp, AstNode* rhsp, bool clockReq) + : AstNodeStmt(fl) { + setNOp1p(lhsp); setNOp2p(rhsp); m_clockReq=clockReq; + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstChangeDet() {} + virtual AstType type() const { return AstType::CHANGEDET;} + virtual AstNode* clone() { return new AstChangeDet(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstNode* lhsp() const { return op1p(); } + AstNode* rhsp() const { return op2p(); } + bool isClockReq() const { return m_clockReq; } + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual int instrCount() const { return widthInstrs(); } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + +struct AstInitial : public AstNode { + AstInitial(FileLine* fl, AstNode* bodysp) + : AstNode(fl) { + addNOp1p(bodysp); + } + virtual ~AstInitial() {} + virtual AstType type() const { return AstType::INITIAL;} + virtual AstNode* clone() { return new AstInitial(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstNode* bodysp() const { return op1p()->castNode(); } // op1 = Expressions to evaluate + // Special accessors + bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); } +}; + +struct AstFinal : public AstNode { + AstFinal(FileLine* fl, AstNode* bodysp) + : AstNode(fl) { + addNOp1p(bodysp); + } + virtual ~AstFinal() {} + virtual AstType type() const { return AstType::FINAL;} + virtual AstNode* clone() { return new AstFinal(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstNode* bodysp() const { return op1p()->castNode(); } // op1 = Expressions to evaluate +}; + +struct AstInitArray : public AstNode { + // Set a var to a large list of values + // The values must be in sorted order, and not exceed the size of the var's array. + // Parents: ASTVAR::init() + // Children: CONSTs... + AstInitArray(FileLine* fl, AstNode* initsp) + : AstNode(fl) { + addNOp1p(initsp); + } + virtual ~AstInitArray() {} + virtual AstType type() const { return AstType::INITARRAY;} + virtual AstNode* clone() { return new AstInitArray(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstNode* initsp() const { return op1p()->castNode(); } // op1 = Initial value expressions + void addInitsp(AstNode* newp) { addOp1p(newp); } +}; + +struct AstPragma : public AstNode { +private: + AstPragmaType m_pragType; // Type of pragma +public: + // Pragmas don't result in any output code, they're just flags that affect + // other processing in verilator. + AstPragma(FileLine* fl, AstPragmaType pragType) + : AstNode(fl) { + m_pragType = pragType; + } + virtual ~AstPragma() {} + virtual AstType type() const { return AstType::PRAGMA;} + virtual AstNode* clone() { return new AstPragma(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstPragmaType pragType() const { return m_pragType; } // *=type of the pragma + virtual V3Hash sameHash() const { return V3Hash(pragType()); } + virtual bool isPredictOptimizable() const { return false; } + virtual bool same(AstNode* samep) const { + return pragType()==samep->castPragma()->pragType(); } +}; + +struct AstStop : public AstNodeStmt { + AstStop(FileLine* fl) + : AstNodeStmt(fl) {} + virtual ~AstStop() {} + virtual AstType type() const { return AstType::STOP;} + virtual AstNode* clone() { return new AstStop(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual bool isSplittable() const { return false; } // SPECIAL: $display has 'visual' ordering + virtual bool isOutputter() const { return true; } // SPECIAL: $display makes output + virtual bool isUnlikely() const { return true; } + virtual int instrCount() const { return 0; } // Rarely executes + virtual V3Hash sameHash() const { return V3Hash(fileline()->lineno()); } + virtual bool same(AstNode* samep) const { + return fileline() == samep->fileline(); } +}; + +struct AstFinish : public AstNodeStmt { + AstFinish(FileLine* fl) + : AstNodeStmt(fl) {} + virtual ~AstFinish() {} + virtual AstType type() const { return AstType::FINISH;} + virtual AstNode* clone() { return new AstFinish(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual bool isSplittable() const { return false; } // SPECIAL: $display has 'visual' ordering + virtual bool isOutputter() const { return true; } // SPECIAL: $display makes output + virtual bool isUnlikely() const { return true; } + virtual int instrCount() const { return 0; } // Rarely executes + virtual V3Hash sameHash() const { return V3Hash(fileline()->lineno()); } + virtual bool same(AstNode* samep) const { + return fileline() == samep->fileline(); } +}; + +struct AstTraceDecl : public AstNodeStmt { + // Trace point declaration + // Separate from AstTraceInc; as a declaration can't be deleted + // Parents: {statement list} + // Children: none +private: + string m_showname; // Name of variable + uint32_t m_code; // Trace identifier code; converted to ASCII by trace routines + uint32_t m_lsb; // Property of var the trace details + uint32_t m_msb; // Property of var the trace details + uint32_t m_arrayLsb; // Property of var the trace details + uint32_t m_arrayMsb; // Property of var the trace details + uint32_t m_codeInc; // Code increment +public: + AstTraceDecl(FileLine* fl, const string& showname, AstVar* varp) + : AstNodeStmt(fl) + , m_showname(showname) { + widthSignedFrom(varp); + m_code = 0; + m_codeInc = varp->arrayElements() * varp->widthWords(); + m_lsb = varp->lsb(); m_msb = varp->msb(); + m_arrayLsb = varp->arrayp(0) ? varp->arrayp(0)->lsbConst() : 0; + m_arrayMsb = varp->arrayp(0) ? varp->arrayp(0)->msbConst() : 0; + } + virtual ~AstTraceDecl() {} + virtual int instrCount() const { return 100; } // Large... + virtual AstType type() const { return AstType::TRACEDECL;} + virtual AstNode* clone() { return new AstTraceDecl(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string name() const { return m_showname; } + string showname() const { return m_showname; } // * = Var name + virtual bool same(AstNode* samep) const { return false; } + // Details on what we're tracing + uint32_t code() const { return m_code; } + void code(uint32_t code) { m_code=code; } + uint32_t codeInc() const { return m_codeInc; } + uint32_t msb() const { return m_msb; } + uint32_t lsb() const { return m_lsb; } + uint32_t arrayMsb() const { return m_arrayMsb; } + uint32_t arrayLsb() const { return m_arrayLsb; } + uint32_t arrayWidth() const { if (!arrayMsb()) return 0; return arrayMsb()-arrayLsb()+1; } +}; + +struct AstTraceInc : public AstNodeStmt { + // Trace point; incremental change detect and dump + // Parents: {statement list} + // Children: incremental value +private: + AstTraceDecl* m_declp; // [After V3Trace] Pointer to declaration +public: + AstTraceInc(FileLine* fl, AstTraceDecl* declp, AstNode* valuep) + : AstNodeStmt(fl) { + widthSignedFrom(declp); + m_declp = declp; + addNOp2p(valuep); + } + virtual ~AstTraceInc() {} + virtual AstType type() const { return AstType::TRACEINC;} + virtual AstNode* clone() { return new AstTraceInc(*this); } + virtual bool broken() const { return !declp()->brokeExists(); } + virtual void cloneRelink() { if (m_declp->clonep()) m_declp = m_declp->clonep()->castTraceDecl(); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str); + virtual int instrCount() const { return 10+2*instrCountLd(); } + virtual V3Hash sameHash() const { return V3Hash(declp()); } + virtual bool same(AstNode* samep) const { + return declp()==samep->castTraceInc()->declp(); } + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual bool isOutputter() const { return true; } + // but isSplittable() true + // op1 = Statements before the value + AstNode* precondsp() const { return op1p()->castNode(); } // op1= prepare statements for condition (exec every loop) + void addPrecondsp(AstNode* newp) { addOp1p(newp); } + // op2 = Value to trace + AstTraceDecl* declp() const { return m_declp; } // Where defined + AstNode* valuep() const { return op2p()->castNode(); } +}; + +struct AstActive : public AstNode { + // Block of code with sensitivity activation + // Parents: MODULE | CFUNC + // Children: SENTREE, statements +private: + string m_name; + AstSenTree* m_sensesp; +public: + AstActive(FileLine* fileline, const string& name, AstSenTree* sensesp) + : AstNode(fileline) { + m_name = name; // Copy it + UASSERT(sensesp, "Sensesp required arg"); + m_sensesp = sensesp; + } + virtual ~AstActive() {} + virtual AstType type() const { return AstType::ACTIVE;} + virtual AstNode* clone() { return new AstActive(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str=cout); + virtual string name() const { return m_name; } + virtual bool broken() const { return (m_sensesp && !m_sensesp->brokeExists()); } + virtual void cloneRelink() { + if (m_sensesp->clonep()) { + m_sensesp = m_sensesp->clonep()->castSenTree(); + UASSERT(m_sensesp, "Bad clone cross link: "<castSenTree(); } + // op2 = Combo logic + AstNode* stmtsp() const { return op2p()->castNode(); } + void addStmtsp(AstNode* nodep) { addOp2p(nodep); } + // METHODS + bool hasInitial() const { return m_sensesp->hasInitial(); } + bool hasSettle() const { return m_sensesp->hasSettle(); } + bool hasClocked() const { return m_sensesp->hasClocked(); } +}; + +class AstAttrOf : public AstNode { + // Return a value of a attribute, for example a LSB or array LSB of a signal + AstAttrType m_attrType; // What sort of extraction + int m_dimension; // Dimension number (0 is leftmost), for ARRAY_LSB extractions +public: + AstAttrOf(FileLine* fl, AstAttrType attrtype, AstNode* fromp, int dimension=0) + : AstNode(fl) { + setOp1p(fromp); m_attrType = attrtype; m_dimension = dimension; } + virtual ~AstAttrOf() {} + virtual AstType type() const { return AstType::ATTROF;} + virtual AstNode* clone() { return new AstAttrOf(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstNode* fromp() const { return op1p(); } + AstAttrType attrType() const { return m_attrType; } + int dimension() const { return m_dimension; } +}; + +//====================================================================== +// non-ary ops + +struct AstRand : public AstNodeTermop { + // Return a random number, based upon width() + AstRand(FileLine* fl, int wwidth) : AstNodeTermop(fl) { + width(wwidth,wwidth); } + virtual ~AstRand() {} + virtual AstType type() const { return AstType::RAND;} + virtual AstNode* clone() { return new AstRand(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string emitVerilog() { return "$random"; } + virtual string emitOperator() { return "VL_RAND_RESET"; } + virtual bool cleanOut() { return true; } + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual int instrCount() const { return instrCountPli(); } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + +struct AstTime : public AstNodeTermop { + AstTime(FileLine* fl) : AstNodeTermop(fl) { + width(64,64); } + virtual ~AstTime() {} + virtual AstType type() const { return AstType::TIME;} + virtual AstNode* clone() { return new AstTime(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string emitVerilog() { return "$time"; } + virtual string emitOperator() { return "VL_TIME"; } + virtual bool cleanOut() { return true; } + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual int instrCount() const { return instrCountTime(); } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + +struct AstUCFunc : public AstNodeTermop { + // User's $c function +public: + AstUCFunc(FileLine* fl, AstNode* exprsp) + : AstNodeTermop(fl) { + addNOp1p(exprsp); + } + virtual ~AstUCFunc() {} + virtual AstType type() const { return AstType::UCFUNC;} + virtual AstNode* clone() { return new AstUCFunc(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool cleanOut() { return false; } + virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially + virtual string emitOperator() { V3ERROR_NA; return ""; } + AstNode* bodysp() const { return op1p()->castNode(); } // op1= expressions to print + virtual bool isSplittable() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isOutputter() const { return true; } + virtual bool isGateOptimizable() const { return false; } + virtual bool isSubstOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual int instrCount() const { return instrCountPli(); } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + +//====================================================================== +// Unary ops + +struct AstUnaryMin : public AstNodeUniop { + AstUnaryMin(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstUnaryMin() {} + virtual AstType type() const { return AstType::UNARYMIN;} + virtual AstNode* clone() { return new AstUnaryMin(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opUnaryMin(lhs); } + virtual string emitVerilog() { return "%k(- %l)"; } + virtual string emitOperator() { return "VL_UNARYMIN"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} + virtual bool sizeMattersLhs() {return true;} +}; +struct AstRedAnd : public AstNodeUniop { + AstRedAnd(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + width(1,1); } + virtual ~AstRedAnd() {} + virtual AstType type() const { return AstType::REDAND;} + virtual AstNode* clone() { return new AstRedAnd(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedAnd(lhs); } + virtual string emitVerilog() { return "%k(& %l)"; } + virtual string emitOperator() { return "VL_REDAND"; } + virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} + virtual bool sizeMattersLhs() {return false;} +}; +struct AstRedOr : public AstNodeUniop { + AstRedOr(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + width(1,1); } + virtual ~AstRedOr() {} + virtual AstType type() const { return AstType::REDOR;} + virtual AstNode* clone() { return new AstRedOr(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedOr(lhs); } + virtual string emitVerilog() { return "%k(| %l)"; } + virtual string emitOperator() { return "VL_REDOR"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} + virtual bool sizeMattersLhs() {return false;} +}; +struct AstRedXor : public AstNodeUniop { + AstRedXor(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + width(1,1); } + virtual ~AstRedXor() {} + virtual AstType type() const { return AstType::REDXOR;} + virtual AstNode* clone() { return new AstRedXor(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedXor(lhs); } + virtual string emitVerilog() { return "%k(^ %l)"; } + virtual string emitOperator() { return "VL_REDXOR"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return (lhsp()->width()!=1 && lhsp()->width()!=2 && lhsp()->width()!=4 + && lhsp()->width()!=8 && lhsp()->width()!=16);} + virtual bool sizeMattersLhs() {return false;} + virtual int instrCount() const { return 1+V3Number::log2b(width()); } +}; +struct AstRedXnor : public AstNodeUniop { + AstRedXnor(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + width(1,1); } + virtual ~AstRedXnor() {} + virtual AstType type() const { return AstType::REDXNOR;} + virtual AstNode* clone() { return new AstRedXnor(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedXnor(lhs); } + virtual string emitVerilog() { return "%k(~^ %l)"; } + virtual string emitOperator() { v3fatalSrc("REDXNOR should have became REDXOR"); return ""; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;} + virtual bool sizeMattersLhs() {return false;} + virtual int instrCount() const { return 1+V3Number::log2b(width()); } +}; + +struct AstLogNot : public AstNodeUniop { + AstLogNot(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + width(1,1); } + virtual ~AstLogNot() {} + virtual AstType type() const { return AstType::LOGNOT;} + virtual AstNode* clone() { return new AstLogNot(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opLogNot(lhs); } + virtual string emitVerilog() { return "%k(! %l)"; } + virtual string emitOperator() { return "VL_LOGNOT"; } + virtual string emitSimpleOperator() { return "!"; } + virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} + virtual bool sizeMattersLhs() {return false;} +}; +struct AstNot : public AstNodeUniop { + AstNot(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstNot() {} + virtual AstType type() const { return AstType::NOT;} + virtual AstNode* clone() { return new AstNot(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opNot(lhs); } + virtual string emitVerilog() { return "%k(~ %l)"; } + virtual string emitOperator() { return "VL_NOT"; } + virtual string emitSimpleOperator() { return "~"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} + virtual bool sizeMattersLhs() {return true;} +}; +struct AstExtend : public AstNodeUniop { + // Expand a value into a wider entity by 0 extension. Width is implied from nodep->width() + AstExtend(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {} + virtual ~AstExtend() {} + virtual AstType type() const { return AstType::EXTEND;} + virtual AstNode* clone() { return new AstExtend(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); } + virtual string emitVerilog() { return "%l"; } + virtual string emitOperator() { return "VL_EXTEND"; } + virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} + virtual bool sizeMattersLhs() {return false;} // Because the EXTEND operator self-casts + virtual int instrCount() const { return 0; } +}; +struct AstExtendS : public AstNodeUniop { + // Expand a value into a wider entity by sign extension. Width is implied from nodep->width() + AstExtendS(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {} + virtual ~AstExtendS() {} + virtual AstType type() const { return AstType::EXTENDS;} + virtual AstNode* clone() { return new AstExtendS(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opExtendS(lhs); } + virtual string emitVerilog() { return "%l"; } + virtual string emitOperator() { return "VL_EXTENDS"; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;} + virtual bool sizeMattersLhs() {return false;} // Because the EXTEND operator self-casts + virtual int instrCount() const { return 0; } + virtual bool signedFlavor() const { return true; } +}; +struct AstSigned : public AstNodeUniop { + // $signed(lhs) + AstSigned(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + isSigned(true); + } + virtual ~AstSigned() {} + virtual AstType type() const { return AstType::SIGNED;} + virtual AstNode* clone() { return new AstSigned(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); out.isSigned(false); } + virtual string emitVerilog() { return "%k$signed(%l)"; } + virtual string emitOperator() { V3ERROR_NA; return ""; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters + virtual bool sizeMattersLhs() {return true;} // Eliminated before matters + virtual int instrCount() const { return 0; } +}; +struct AstUnsigned : public AstNodeUniop { + // $unsigned(lhs) + AstUnsigned(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + isSigned(false); + } + virtual ~AstUnsigned() {} + virtual AstType type() const { return AstType::UNSIGNED;} + virtual AstNode* clone() { return new AstUnsigned(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); out.isSigned(false); } + virtual string emitVerilog() { return "%k$unsigned(%l)"; } + virtual string emitOperator() { V3ERROR_NA; return ""; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters + virtual bool sizeMattersLhs() {return true;} // Eliminated before matters + virtual int instrCount() const { return 0; } +}; +struct AstCountOnes : public AstNodeUniop { + // Number of bits set in vector + AstCountOnes(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {} + virtual ~AstCountOnes() {} + virtual AstType type() const { return AstType::COUNTONES;} + virtual AstNode* clone() { return new AstCountOnes(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opCountOnes(lhs); } + virtual string emitVerilog() { return "%k$countones(%l)"; } + virtual bool emitWordForm() { return true; } + virtual string emitOperator() { return "VL_COUNTONES"; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;} + virtual bool sizeMattersLhs() {return false;} + virtual int instrCount() const { return widthInstrs()*16; } +}; +struct AstIsUnknown : public AstNodeUniop { + // True if any unknown bits + AstIsUnknown(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + width(1,1);} + virtual ~AstIsUnknown() {} + virtual AstType type() const { return AstType::ISUNKNOWN;} + virtual AstNode* clone() { return new AstIsUnknown(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opIsUnknown(lhs); } + virtual string emitVerilog() { return "%k$isunknown(%l)"; } + virtual string emitOperator() { V3ERROR_NA; return ""; } + virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} + virtual bool sizeMattersLhs() {return false;} +}; +struct AstOneHot : public AstNodeUniop { + // True if only single bit set in vector + AstOneHot(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + width(1,1);} + virtual ~AstOneHot() {} + virtual AstType type() const { return AstType::ONEHOT;} + virtual AstNode* clone() { return new AstOneHot(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opOneHot(lhs); } + virtual string emitVerilog() { return "%k$onehot(%l)"; } + virtual bool emitWordForm() { return true; } + virtual string emitOperator() { return "VL_ONEHOT"; } + virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} + virtual bool sizeMattersLhs() {return false;} + virtual int instrCount() const { return widthInstrs()*4; } +}; +struct AstOneHot0 : public AstNodeUniop { + // True if only single bit, or no bits set in vector + AstOneHot0(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { + width(1,1);} + virtual ~AstOneHot0() {} + virtual AstType type() const { return AstType::ONEHOT0;} + virtual AstNode* clone() { return new AstOneHot0(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opOneHot0(lhs); } + virtual string emitVerilog() { return "%k$onehot0(%l)"; } + virtual bool emitWordForm() { return true; } + virtual string emitOperator() { return "VL_ONEHOT0"; } + virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} + virtual bool sizeMattersLhs() {return false;} + virtual int instrCount() const { return widthInstrs()*3; } +}; + +struct AstCast : public AstNodeUniop { + // Cast to appropriate data type +private: + int m_size; +public: + AstCast(FileLine* fl, AstNode* lhsp, int setwidth) : AstNodeUniop(fl, lhsp) { + m_size=setwidth; + if (setwidth) { width(setwidth,setwidth); } + } + AstCast(FileLine* fl, AstNode* lhsp, AstNode* widthFromp) : AstNodeUniop(fl, lhsp) { + if (widthFromp) { widthSignedFrom(widthFromp); } + m_size=width(); + } + virtual ~AstCast() {} + virtual AstType type() const { return AstType::CAST;} + virtual AstNode* clone() { return new AstCast(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); } + virtual string emitVerilog() { return "%k$_CAST(%l)"; } + virtual string emitOperator() { return "VL_CAST"; } + virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} + virtual bool sizeMattersLhs() {return false;} // Special cased in V3Cast + virtual V3Hash sameHash() const { return V3Hash(size()); } + virtual bool same(AstNode* samep) const { return size()==samep->castCast()->size(); } + virtual void dump(ostream& str=cout); + // + int size() const { return m_size; } +}; + +//====================================================================== +// Binary ops + +struct AstLogOr : public AstNodeBiComAsv { + AstLogOr(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstLogOr() {} + virtual AstType type() const { return AstType::LOGOR;} + virtual AstNode* clone() { return new AstLogOr(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogOr(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k|| %r)"; } + virtual string emitOperator() { return "VL_LOGOR"; } + virtual string emitSimpleOperator() { return "||"; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } +}; +struct AstLogAnd : public AstNodeBiComAsv { + AstLogAnd(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstLogAnd() {} + virtual AstType type() const { return AstType::LOGAND;} + virtual AstNode* clone() { return new AstLogAnd(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogAnd(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k&& %r)"; } + virtual string emitOperator() { return "VL_LOGAND"; } + virtual string emitSimpleOperator() { return "&&"; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } +}; +struct AstLogIf : public AstNodeBiop { + AstLogIf(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstLogIf() {} + virtual AstType type() const { return AstType::LOGIF;} + virtual AstNode* clone() { return new AstLogIf(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { V3ERROR_NA; } + virtual string emitVerilog() { return "%k(%l %k-> %r)"; } + virtual string emitOperator() { return "VL_LOGIF"; } + virtual string emitSimpleOperator() { return "->"; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } +}; +struct AstLogIff : public AstNodeBiCom { + AstLogIff(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstLogIff() {} + virtual AstType type() const { return AstType::LOGIFF;} + virtual AstNode* clone() { return new AstLogIff(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { V3ERROR_NA; } + virtual string emitVerilog() { return "%k(%l %k<-> %r)"; } + virtual string emitOperator() { return "VL_LOGIFF"; } + virtual string emitSimpleOperator() { return "<->"; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } +}; +struct AstOr : public AstNodeBiComAsv { + AstOr(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstOr() {} + virtual AstType type() const { return AstType::OR;} + virtual AstNode* clone() { return new AstOr(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opOr(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k| %r)"; } + virtual string emitOperator() { return "VL_OR"; } + virtual string emitSimpleOperator() { return "|"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {V3ERROR_NA; return false;} // Lclean && Rclean + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstAnd : public AstNodeBiComAsv { + AstAnd(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstAnd() {} + virtual AstType type() const { return AstType::AND;} + virtual AstNode* clone() { return new AstAnd(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAnd(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k& %r)"; } + virtual string emitOperator() { return "VL_AND"; } + virtual string emitSimpleOperator() { return "&"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {V3ERROR_NA; return false;} // Lclean || Rclean + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstXor : public AstNodeBiComAsv { + AstXor(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstXor() {} + virtual AstType type() const { return AstType::XOR;} + virtual AstNode* clone() { return new AstXor(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opXor(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k^ %r)"; } + virtual string emitOperator() { return "VL_XOR"; } + virtual string emitSimpleOperator() { return "^"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} // Lclean && Rclean + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstXnor : public AstNodeBiComAsv { + AstXnor(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstXnor() {} + virtual AstType type() const { return AstType::XNOR;} + virtual AstNode* clone() { return new AstXnor(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opXnor(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k^ ~ %r)"; } + virtual string emitOperator() { return "VL_XNOR"; } + virtual string emitSimpleOperator() { return "^ ~"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} +}; +struct AstEq : public AstNodeBiCom { + AstEq(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstEq() {} + virtual AstType type() const { return AstType::EQ;} + virtual AstNode* clone() { return new AstEq(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opEq(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k== %r)"; } + virtual string emitOperator() { return "VL_EQ"; } + virtual string emitSimpleOperator() { return "=="; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstNeq : public AstNodeBiCom { + AstNeq(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstNeq() {} + virtual AstType type() const { return AstType::NEQ;} + virtual AstNode* clone() { return new AstNeq(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opNeq(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k!= %r)"; } + virtual string emitOperator() { return "VL_NEQ"; } + virtual string emitSimpleOperator() { return "!="; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstLt : public AstNodeBiop { + AstLt(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstLt() {} + virtual AstType type() const { return AstType::LT;} + virtual AstNode* clone() { return new AstLt(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLt(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k< %r)"; } + virtual string emitOperator() { return "VL_LT"; } + virtual string emitSimpleOperator() { return "<"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstLtS : public AstNodeBiop { + AstLtS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstLtS() {} + virtual AstType type() const { return AstType::LTS;} + virtual AstNode* clone() { return new AstLtS(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLtS(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k< %r)"; } + virtual string emitOperator() { return "VL_LTS"; } + virtual string emitSimpleOperator() { return ""; } + virtual bool emitWordForm() { return false; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool signedFlavor() const { return true; } +}; +struct AstGt : public AstNodeBiop { + AstGt(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstGt() {} + virtual AstType type() const { return AstType::GT;} + virtual AstNode* clone() { return new AstGt(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGt(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k> %r)"; } + virtual string emitOperator() { return "VL_GT"; } + virtual string emitSimpleOperator() { return ">"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstGtS : public AstNodeBiop { + AstGtS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstGtS() {} + virtual AstType type() const { return AstType::GTS;} + virtual AstNode* clone() { return new AstGtS(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGtS(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k> %r)"; } + virtual string emitOperator() { return "VL_GTS"; } + virtual string emitSimpleOperator() { return ""; } + virtual bool emitWordForm() { return false; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool signedFlavor() const { return true; } +}; +struct AstGte : public AstNodeBiop { + AstGte(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstGte() {} + virtual AstType type() const { return AstType::GTE;} + virtual AstNode* clone() { return new AstGte(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGte(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k>= %r)"; } + virtual string emitOperator() { return "VL_GTE"; } + virtual string emitSimpleOperator() { return ">="; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstGteS : public AstNodeBiop { + AstGteS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstGteS() {} + virtual AstType type() const { return AstType::GTES;} + virtual AstNode* clone() { return new AstGteS(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGteS(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k>= %r)"; } + virtual string emitOperator() { return "VL_GTES"; } + virtual string emitSimpleOperator() { return ""; } + virtual bool emitWordForm() { return false; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool signedFlavor() const { return true; } +}; +struct AstLte : public AstNodeBiop { + AstLte(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstLte() {} + virtual AstType type() const { return AstType::LTE;} + virtual AstNode* clone() { return new AstLte(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLte(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k<= %r)"; } + virtual string emitOperator() { return "VL_LTE"; } + virtual string emitSimpleOperator() { return "<="; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstLteS : public AstNodeBiop { + AstLteS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstLteS() {} + virtual AstType type() const { return AstType::LTES;} + virtual AstNode* clone() { return new AstLteS(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLteS(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k<= %r)"; } + virtual string emitOperator() { return "VL_LTES"; } + virtual string emitSimpleOperator() { return ""; } + virtual bool emitWordForm() { return false; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool signedFlavor() const { return true; } +}; +struct AstShiftL : public AstNodeBiop { + AstShiftL(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth=0) + : AstNodeBiop(fl, lhsp, rhsp) { + if (setwidth) { width(setwidth,setwidth); } + } + virtual ~AstShiftL() {} + virtual AstType type() const { return AstType::SHIFTL;} + virtual AstNode* clone() { return new AstShiftL(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opShiftL(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k<< %r)"; } + virtual string emitOperator() { return "VL_SHIFTL"; } + virtual string emitSimpleOperator() { return "<<"; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstShiftR : public AstNodeBiop { + AstShiftR(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth=0) + : AstNodeBiop(fl, lhsp, rhsp) { + if (setwidth) { width(setwidth,setwidth); } + } + virtual ~AstShiftR() {} + virtual AstType type() const { return AstType::SHIFTR;} + virtual AstNode* clone() { return new AstShiftR(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opShiftR(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k>> %r)"; } + virtual string emitOperator() { return "VL_SHIFTR"; } + virtual string emitSimpleOperator() { return ">>"; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} // LHS size might be > output size, so don't want to force size +}; +struct AstShiftRS : public AstNodeBiop { + AstShiftRS(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth=0) + : AstNodeBiop(fl, lhsp, rhsp) { + if (setwidth) { width(setwidth,setwidth); } + } + virtual ~AstShiftRS() {} + virtual AstType type() const { return AstType::SHIFTRS;} + virtual AstNode* clone() { return new AstShiftRS(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opShiftRS(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k>>> %r)"; } + virtual string emitOperator() { return "VL_SHIFTRS"; } + virtual string emitSimpleOperator() { return ""; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool signedFlavor() const { return true; } +}; +struct AstAdd : public AstNodeBiComAsv { + AstAdd(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstAdd() {} + virtual AstType type() const { return AstType::ADD;} + virtual AstNode* clone() { return new AstAdd(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAdd(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k+ %r)"; } + virtual string emitOperator() { return "VL_ADD"; } + virtual string emitSimpleOperator() { return "+"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} +}; +struct AstSub : public AstNodeBiop { + AstSub(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstSub() {} + virtual AstType type() const { return AstType::SUB;} + virtual AstNode* clone() { return new AstSub(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opSub(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k- %r)"; } + virtual string emitOperator() { return "VL_SUB"; } + virtual string emitSimpleOperator() { return "-"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} +}; +struct AstMul : public AstNodeBiComAsv { + AstMul(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstMul() {} + virtual AstType type() const { return AstType::MUL;} + virtual AstNode* clone() { return new AstMul(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opMul(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k* %r)"; } + virtual string emitOperator() { return "VL_MUL"; } + virtual string emitSimpleOperator() { return "*"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} + virtual int instrCount() const { return widthInstrs()*instrCountMul(); } +}; +struct AstMulS : public AstNodeBiComAsv { + AstMulS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstMulS() {} + virtual AstType type() const { return AstType::MULS;} + virtual AstNode* clone() { return new AstMulS(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opMulS(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k* %r)"; } + virtual string emitOperator() { return "VL_MULS"; } + virtual string emitSimpleOperator() { return ""; } + virtual bool emitWordForm() { return false; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} + virtual int instrCount() const { return widthInstrs()*instrCountMul(); } + virtual bool signedFlavor() const { return true; } +}; +struct AstDiv : public AstNodeBiop { + AstDiv(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstDiv() {} + virtual AstType type() const { return AstType::DIV;} + virtual AstNode* clone() { return new AstDiv(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opDiv(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k/ %r)"; } + virtual string emitOperator() { return "VL_DIV"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} + virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } +}; +struct AstDivS : public AstNodeBiop { + AstDivS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstDivS() {} + virtual AstType type() const { return AstType::DIVS;} + virtual AstNode* clone() { return new AstDivS(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opDivS(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k/ %r)"; } + virtual string emitOperator() { return "VL_DIVS"; } + virtual bool emitWordForm() { return false; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} + virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } + virtual bool signedFlavor() const { return true; } +}; +struct AstModDiv : public AstNodeBiop { + AstModDiv(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstModDiv() {} + virtual AstType type() const { return AstType::MODDIV;} + virtual AstNode* clone() { return new AstModDiv(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opModDiv(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k%% %r)"; } + virtual string emitOperator() { return "VL_MODDIV"; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} + virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } +}; +struct AstModDivS : public AstNodeBiop { + AstModDivS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstModDivS() {} + virtual AstType type() const { return AstType::MODDIVS;} + virtual AstNode* clone() { return new AstModDivS(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opModDivS(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k%% %r)"; } + virtual string emitOperator() { return "VL_MODDIVS"; } + virtual bool emitWordForm() { return false; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} + virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } + virtual bool signedFlavor() const { return true; } +}; +struct AstPow : public AstNodeBiop { + AstPow(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstPow() {} + virtual AstType type() const { return AstType::POW;} + virtual AstNode* clone() { return new AstPow(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPow(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k** %r)"; } + virtual string emitOperator() { return "VL_POW"; } + virtual bool emitWordForm() { return false; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return widthInstrs()*instrCountMul(); } +}; +struct AstPowS : public AstNodeBiop { + AstPowS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + virtual ~AstPowS() {} + virtual AstType type() const { return AstType::POWS;} + virtual AstNode* clone() { return new AstPowS(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPowS(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k** %r)"; } + virtual string emitOperator() { return "VL_POWS"; } + virtual bool emitWordForm() { return false; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return widthInstrs()*instrCountMul(); } + virtual bool signedFlavor() const { return true; } +}; +struct AstEqCase : public AstNodeBiCom { + AstEqCase(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstEqCase() {} + virtual AstType type() const { return AstType::EQCASE;} + virtual AstNode* clone() { return new AstEqCase(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opCaseEq(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k=== %r)"; } + virtual string emitOperator() { return "VL_EQ"; } // Until have 4 state anyways + virtual string emitSimpleOperator() { return "=="; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstNeqCase : public AstNodeBiCom { + AstNeqCase(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { + width(1,1); } + virtual ~AstNeqCase() {} + virtual AstType type() const { return AstType::NEQCASE;} + virtual AstNode* clone() { return new AstNeqCase(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opCaseNeq(lhs,rhs); } + virtual string emitVerilog() { return "%k(%l %k!== %r)"; } + virtual string emitOperator() { return "VL_NEQ"; } // Until have 4 state anyways + virtual string emitSimpleOperator() { return "!="; } + virtual bool emitWordForm() { return true; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; +struct AstConcat : public AstNodeBiop { + // If you're looking for {#{}}, see AstReplicate + AstConcat(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + if (lhsp->width() && rhsp->width()) width(lhsp->width()+rhsp->width(),lhsp->width()+rhsp->width()); + } + virtual ~AstConcat() {} + virtual AstType type() const { return AstType::CONCAT;} + virtual AstNode* clone() { return new AstConcat(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string emitVerilog() { return "%k{%l, %k%r}"; } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opConcat(lhs,rhs); } + virtual string emitOperator() { return "VL_CONCAT"; } + virtual bool cleanOut() {return true;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return widthInstrs()*2; } +}; +struct AstReplicate : public AstNodeBiop { + AstReplicate(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {} + AstReplicate(FileLine* fl, AstNode* lhsp, uint32_t repCount) + : AstNodeBiop(fl, lhsp, new AstConst(fl, repCount)) {} + virtual ~AstReplicate() {} + virtual AstType type() const { return AstType::REPLICATE;} + virtual AstNode* clone() { return new AstReplicate(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opRepl(lhs,rhs); } + virtual string emitVerilog() { return "%k{%l{%k%r}}"; } + virtual string emitOperator() { return "VL_REPLICATE"; } + virtual bool cleanOut() {return false;} + virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual int instrCount() const { return widthInstrs()*2; } +}; + +//====================================================================== +// PSL + +struct AstPslDefClock : public AstNode { + // Set default PSL clock + // Parents: MODULE + // Children: SENITEM +public: + AstPslDefClock(FileLine* fl, AstSenItem* sensesp) + : AstNode(fl) { + addNOp1p(sensesp); + } + virtual ~AstPslDefClock() {} + virtual AstType type() const { return AstType::PSLDEFCLOCK;} + virtual AstNode* clone() { return new AstPslDefClock(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstSenItem* sensesp() const { return op1p()->castSenItem(); } // op1 = Sensitivity list +}; + +struct AstPslClocked : public AstNode { + // A clocked property + // Parents: ASSERT|COVER (property) + // Children: SENITEM, Properties +public: + AstPslClocked(FileLine* fl, AstSenItem* sensesp, AstNode* propp) + : AstNode(fl) { + addNOp1p(sensesp); + addOp2p(propp); + } + virtual ~AstPslClocked() {} + virtual AstType type() const { return AstType::PSLCLOCKED;} + virtual AstNode* clone() { return new AstPslClocked(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstSenItem* sensesp() const { return op1p()->castSenItem(); } // op1 = Sensitivity list + AstNode* propp() const { return op2p(); } // op2 = property +}; + +struct AstPslAssert : public AstNodeStmt { + // Psl Assertion + // Parents: {statement list} + // Children: expression, report string +private: + string m_name; // Name to report +public: + AstPslAssert(FileLine* fl, AstNode* propp, const string& name="") + : AstNodeStmt(fl) + , m_name(name) { + addOp1p(propp); + } + virtual ~AstPslAssert() {} + virtual AstType type() const { return AstType::PSLASSERT;} + virtual AstNode* clone() { return new AstPslAssert(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string name() const { return m_name; } // * = Var name + virtual V3Hash sameHash() const { return V3Hash(name()); } + virtual bool same(AstNode* samep) const { return samep->name() == name(); } + AstNode* propp() const { return op1p(); } // op1 = property + AstSenTree* sentreep() const { return op2p()->castSenTree(); } // op2 = clock domain + void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain +}; + +struct AstPslCover : public AstNodeStmt { + // Psl Cover + // Parents: {statement list} + // Children: expression, report string +private: + string m_name; // Name to report +public: + AstPslCover(FileLine* fl, AstNode* propp, const string& name="") + : AstNodeStmt(fl) + , m_name(name) { + addOp1p(propp); + } + virtual ~AstPslCover() {} + virtual AstType type() const { return AstType::PSLCOVER;} + virtual AstNode* clone() { return new AstPslCover(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string name() const { return m_name; } // * = Var name + virtual V3Hash sameHash() const { return V3Hash(name()); } + virtual bool same(AstNode* samep) const { return samep->name() == name(); } + AstNode* propp() const { return op1p(); } // op1 = property + AstSenTree* sentreep() const { return op2p()->castSenTree(); } // op2 = clock domain + void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain + AstNode* coverincp() const { return op3p(); } // op3 = coverage node + void coverincp(AstCoverInc* nodep) { addOp3p(nodep); } // op3 = coverage node +}; + +//====================================================================== +// PSL Expressions + +struct AstPslBool : public AstNode { + // Separates PSL Sere/sequences from the normal expression boolean layer below. + // Note this excludes next() and similar functions; they are time domain, so not under AstPslBool. + // Parents: Sequences, etc. + // Children: math + AstPslBool(FileLine* fileline, AstNode* exprp) + : AstNode(fileline) { + addOp1p(exprp); + } + virtual ~AstPslBool() {} + virtual AstType type() const { return AstType::PSLBOOL;} + virtual AstNode* clone() { return new AstPslBool(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstNode* exprp() const { return op1p()->castNode(); } // op1= expression + virtual bool isGateOptimizable() const { return false; } // Not relevant + virtual bool isPredictOptimizable() const { return false; } // Not relevant + virtual int instrCount() const { return 0; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + +//====================================================================== +// Text based nodes + +struct AstText : public AstNodeText { + AstText(FileLine* fl, const string& textp) + : AstNodeText(fl, textp) {} + virtual ~AstText() {} + virtual AstType type() const { return AstType::TEXT;} + virtual AstNode* clone() { return new AstText(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } +}; + +struct AstScCtor : public AstNodeText { + AstScCtor(FileLine* fl, const string& textp) + : AstNodeText(fl, textp) {} + virtual ~AstScCtor() {} + virtual AstType type() const { return AstType::SCCTOR;} + virtual AstNode* clone() { return new AstScCtor(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool isSplittable() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isOutputter() const { return true; } +}; + +struct AstScHdr : public AstNodeText { + AstScHdr(FileLine* fl, const string& textp) + : AstNodeText(fl, textp) {} + virtual ~AstScHdr() {} + virtual AstType type() const { return AstType::SCHDR;} + virtual AstNode* clone() { return new AstScHdr(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool isSplittable() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isOutputter() const { return true; } +}; + +struct AstScImp : public AstNodeText { + AstScImp(FileLine* fl, const string& textp) + : AstNodeText(fl, textp) {} + virtual ~AstScImp() {} + virtual AstType type() const { return AstType::SCIMP;} + virtual AstNode* clone() { return new AstScImp(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool isSplittable() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isOutputter() const { return true; } +}; + +struct AstScImpHdr : public AstNodeText { + AstScImpHdr(FileLine* fl, const string& textp) + : AstNodeText(fl, textp) {} + virtual ~AstScImpHdr() {} + virtual AstType type() const { return AstType::SCIMPHDR;} + virtual AstNode* clone() { return new AstScImpHdr(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool isSplittable() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isOutputter() const { return true; } +}; + +struct AstScInt : public AstNodeText { + AstScInt(FileLine* fl, const string& textp) + : AstNodeText(fl, textp) {} + virtual ~AstScInt() {} + virtual AstType type() const { return AstType::SCINT;} + virtual AstNode* clone() { return new AstScInt(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool isSplittable() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isOutputter() const { return true; } +}; + +struct AstUCStmt : public AstNodeStmt { + // User $c statement + AstUCStmt(FileLine* fl, AstNode* exprsp) + : AstNodeStmt(fl) { + addNOp1p(exprsp); + } + virtual ~AstUCStmt() {} + virtual AstType type() const { return AstType::UCSTMT;} + virtual AstNode* clone() { return new AstUCStmt(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstNode* bodysp() const { return op1p()->castNode(); } // op1= expressions to print + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual bool isSplittable() const { return false; } + virtual bool isOutputter() const { return true; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + +//====================================================================== +// Emit C nodes + +struct AstCFile : public AstNode { + // C++ output file + // Parents: NETLIST + // Children: nothing yet +private: + string m_name; ///< Filename + bool m_slow:1; ///< Compile w/o optimization + bool m_source:1; ///< Source file (vs header file) + bool m_support:1; ///< Support file (non systemc) +public: + AstCFile(FileLine* fl, const string& name) + : AstNode(fl) { + m_name = name; + m_slow = false; + m_source = false; + m_support = false; + } + virtual ~AstCFile() {} + virtual AstType type() const { return AstType::CFILE;} + virtual AstNode* clone() { return new AstCFile(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string name() const { return m_name; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } + virtual void dump(ostream& str=cout); + bool slow() const { return m_slow; } + void slow(bool flag) { m_slow = flag; } + bool source() const { return m_source; } + void source(bool flag) { m_source = flag; } + bool support() const { return m_support; } + void support(bool flag) { m_support = flag; } +}; + +struct AstCFunc : public AstNode { + // C++ function + // Parents: MODULE/SCOPE + // Children: VAR/statements +private: + AstCFuncType m_funcType; + AstScope* m_scopep; + string m_name; + string m_rtnType; // void, bool, or other return type + string m_argTypes; + bool m_dontCombine:1; // V3Combine shouldn't compare this func tree, it's special + bool m_skipDecl:1; // Don't declare it + bool m_declPrivate:1; // Declare it private + bool m_formCallTree:1; // Make a global function to call entire tree of functions + bool m_slow:1; // Slow routine, called once or just at init time + bool m_funcPublic:1; // From user public task/function +public: + AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType="") + : AstNode(fl) { + m_funcType = AstCFuncType::NORMAL; + m_scopep = scopep; + m_name = name; + m_rtnType = rtnType; + m_dontCombine = false; + m_skipDecl = false; + m_declPrivate = false; + m_formCallTree = false; + m_slow = false; + m_funcPublic = false; + } + virtual ~AstCFunc() {} + virtual AstType type() const { return AstType::CFUNC;} + virtual AstNode* clone() { return new AstCFunc(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual string name() const { return m_name; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } + // + void name(const string& flag) { m_name = flag; } + AstScope* scopep() const { return m_scopep; } + void scopep(AstScope* nodep) { m_scopep = nodep; } + string rtnTypeVoid() const { return ((m_rtnType=="") ? "void" : m_rtnType); } + bool dontCombine() const { return m_dontCombine || funcType()!=AstCFuncType::NORMAL; } + void dontCombine(bool flag) { m_dontCombine = flag; } + bool skipDecl() const { return m_skipDecl; } + void skipDecl(bool flag) { m_skipDecl = flag; } + bool declPrivate() const { return m_declPrivate; } + void declPrivate(bool flag) { m_declPrivate = flag; } + bool formCallTree() const { return m_formCallTree; } + void formCallTree(bool flag) { m_formCallTree = flag; } + bool slow() const { return m_slow; } + void slow(bool flag) { m_slow = flag; } + bool funcPublic() const { return m_funcPublic; } + void funcPublic(bool flag) { m_funcPublic = flag; } + void argTypes(const string& str) { m_argTypes = str; } + string argTypes() const { return m_argTypes; } + void funcType(AstCFuncType flag) { m_funcType = flag; } + AstCFuncType funcType() const { return m_funcType; } + // + // If adding node accessors, see below + AstNode* argsp() const { return op1p()->castNode(); } + void addArgsp(AstNode* nodep) { addOp1p(nodep); } + AstNode* initsp() const { return op2p()->castNode(); } + void addInitsp(AstNode* nodep) { addOp2p(nodep); } + AstNode* stmtsp() const { return op3p()->castNode(); } + void addStmtsp(AstNode* nodep) { addOp3p(nodep); } + AstNode* finalsp() const { return op4p()->castNode(); } + void addFinalsp(AstNode* nodep) { addOp4p(nodep); } + // Special methods + bool emptyBody() const { return argsp()==NULL && initsp()==NULL && stmtsp()==NULL && finalsp()==NULL; } +}; + +struct AstCCall : public AstNodeStmt { + // C++ function call + // Parents: Anything above a statement + // Children: Args to the function +private: + AstCFunc* m_funcp; + string m_hiername; +public: + AstCCall(FileLine* fl, AstCFunc* funcp, AstNode* argsp=NULL) + : AstNodeStmt(fl) { + m_funcp = funcp; + addNOp1p(argsp); + } + AstCCall(AstCCall* oldp, AstCFunc* funcp) // Replacement form for V3Combine + : AstNodeStmt(oldp->fileline()) { + m_funcp = funcp; + m_hiername = oldp->hiername(); + } + virtual ~AstCCall() {} + virtual AstType type() const { return AstType::CCALL;} + virtual AstNode* clone() { return new AstCCall(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual void dump(ostream& str=cout); + virtual void cloneRelink() { if (m_funcp && m_funcp->clonep()) { + m_funcp = m_funcp->clonep()->castCFunc(); + }} + virtual bool broken() const { return (m_funcp && !m_funcp->brokeExists()); } + virtual int instrCount() const { return instrCountCall(); } + virtual V3Hash sameHash() const { return V3Hash(funcp()); } + virtual bool same(AstNode* samep) const { + return funcp()==samep->castCCall()->funcp(); } + AstNode* exprsp() const { return op1p()->castNode(); } // op1= expressions to print + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual bool isSplittable() const { return false; } // SPECIAL: $display has 'visual' ordering + virtual bool isOutputter() const { return true; } + AstCFunc* funcp() const { return m_funcp; } + string hiername() const { return m_hiername; } + void hiername(const string& hn) { m_hiername = hn; } + // + AstNode* argsp() const { return op1p()->castNode(); } + void addArgsp(AstNode* nodep) { addOp1p(nodep); } +}; + +struct AstCReturn : public AstNodeStmt { + // C++ return from a function + // Parents: CFUNC/statement + // Children: Math +public: + AstCReturn(FileLine* fl, AstNode* lhsp) + : AstNodeStmt(fl) { + setOp1p(lhsp); + } + virtual ~AstCReturn() {} + virtual AstType type() const { return AstType::CRETURN;} + virtual AstNode* clone() { return new AstCReturn(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual int instrCount() const { return widthInstrs(); } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode*) const { return true; } + // + AstNode* lhsp() const { return op1p(); } +}; + +struct AstCInclude : public AstNode { + // C++ use of another class + // Parents: MODULE + // Children: None +private: + AstModule* m_modp; +public: + AstCInclude(FileLine* fl, AstModule* modp) + : AstNode(fl) { + m_modp = modp; + } + virtual ~AstCInclude() {} + virtual AstType type() const { return AstType::CINCLUDE;} + virtual AstNode* clone() { return new AstCInclude(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + virtual bool broken() const { return (m_modp && !m_modp->brokeExists()); } + virtual void cloneRelink() { if (m_modp && m_modp->clonep()) { + m_modp = m_modp->clonep()->castModule(); + }} + AstModule* modp() const { return m_modp; } +}; + +struct AstCMath : public AstNodeMath { + // Emit C textual math function (like AstUCFunc) + AstCMath(FileLine* fl, AstNode* exprsp) + : AstNodeMath(fl) { + addOp1p(exprsp); + widthSignedFrom(exprsp); + } + AstCMath(FileLine* fl, const string& textStmt, int setwidth) + : AstNodeMath(fl) { + addNOp1p(new AstText(fl, textStmt)); + if (setwidth) { width(setwidth,setwidth); } + } + virtual ~AstCMath() {} + virtual AstType type() const { return AstType::CMATH;} + virtual AstNode* clone() { return new AstCMath(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstNode* bodysp() const { return op1p()->castNode(); } // op1= expressions to print + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual bool cleanOut() { return true; } + virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially + virtual string emitOperator() { V3ERROR_NA; return ""; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + + +struct AstCStmt : public AstNodeStmt { + // Emit C statement + AstCStmt(FileLine* fl, AstNode* exprsp) + : AstNodeStmt(fl) { + addNOp1p(exprsp); + } + AstCStmt(FileLine* fl, const string& textStmt) + : AstNodeStmt(fl) { + addNOp1p(new AstText(fl, textStmt)); + } + virtual ~AstCStmt() {} + virtual AstType type() const { return AstType::CSTMT;} + virtual AstNode* clone() { return new AstCStmt(*this); } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstNode* bodysp() const { return op1p()->castNode(); } // op1= expressions to print + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return false; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } +}; + +//###################################################################### +// Top + +struct AstNetlist : public AstNode { + // All modules are under this single top node. + // Parents: none + // Children: MODULEs & CFILEs + AstNetlist() : AstNode(new FileLine("AstRoot",0)) {} + virtual ~AstNetlist() {} + virtual AstType type() const { return AstType::NETLIST;} + virtual AstNode* clone() { v3fatalSrc("Can't clone top-level netlist\n"); return NULL; } + virtual void accept(AstNVisitor& v, AstNUser* vup=NULL) { v.visit(this,vup); } + AstModule* modulesp() const { return op1p()->castModule();} // op1 = List of modules + AstModule* topModulep() const { return op1p()->castModule(); } // * = Top module in hierarchy (first one added, for now) + void addModulep(AstModule* modulep) { addOp1p(modulep); } + AstCFile* filesp() const { return op2p()->castCFile();} // op2 = List of files + void addFilesp(AstCFile* filep) { addOp2p(filep); } +}; + +//###################################################################### + +#endif // Guard diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp new file mode 100644 index 000000000..c540cd68b --- /dev/null +++ b/src/V3Begin.cpp @@ -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 +#include +#include +#include +#include + +#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," "<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 "<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 "<name(m_beginScope+"__DOT__"+nodep->name()); + UINFO(8," rename to "<name()<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__<<": "< +#include +#include +#include + +#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: "<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: "<iterateChildren(*this); + } + +public: + // CONSTUCTORS + BranchVisitor(AstNetlist* rootp) { + reset(); + rootp->iterateChildren(*this); + } + virtual ~BranchVisitor() {} +}; + +//###################################################################### +// Branch class functions + +void V3Branch::branchAll(AstNetlist* rootp) { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include +#include + +#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 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__<<": "< 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 +#include +#include +#include + +#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<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: "<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<ignoreOverlap() && !bitched) { + itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<condsp()) { // Case statement's default... Fill the table + for (uint32_t i=0; i<(1UL<v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<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<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<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<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__<<": "<, or >= +// +// Signed values are always sign extended on promotion or right shift, +// even if assigning to a unsigned. +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include + +#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 "<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 out = - (alhsp(), 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__<<": "< +#include +#include +#include +#include + +#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): "<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 "<isTop()) { + m_topModp = nodep; + } + nodep->iterateChildren(*this); + } + virtual void visit(AstTopScope* nodep, AstNUser*) { + UINFO(4," TS "<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 "<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__<<": "< +#include +#include +#include + +#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 "<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__<<": "< +#include +#include +#include + +#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: "<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: "<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 "<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 "<iterateChildren(*this); + } + virtual void visit(AstScope* nodep, AstNUser*) { + //UINFO(4," SCOPE "<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 "<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 "<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 "<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__<<": "< 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 +#include +#include +#include +#include +#include + +#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 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 "< "< 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 "<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 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 "<user4()<<" "<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 "<user4p())<<" "<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 "<user4p())<<" "<user4p())<<" "<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 "<funcp()); nodep=NULL; + } + } + } + + void walkDupCodeStart(AstNode* node1p) { + V3Hash hashval (node1p->user4p()); + //UINFO(4," STMT "< 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 "<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 "<user4p())<<" "<user4p())<<" "<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 "<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__<<": "< +#include +#include +#include + +#include "V3Global.h" +#include "V3Const.h" +#include "V3Read.h" +#include "V3Ast.h" +#include "V3Width.h" +#include "V3Signed.h" + +//###################################################################### +// Const state, as a visitor of each AstNode + +class ConstVisitor : public AstNVisitor { +private: + // STATE + bool m_params; // If true, propogate parameterized and true numbers only + bool m_required; // If true, must become a constant + bool m_wremove; // Inside scope, no assignw removal + bool m_warn; // Output warnings + bool m_cpp; // C++ conversions only + AstModule* m_modp; // Current module + AstNode* m_scopep; // Current scope + //int debug() { return 9; } + + // METHODS + bool operandConst (AstNode* nodep) { + return (nodep->castConst()); + } + bool operandAsvConst (AstNode* nodep) { + // BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...) + AstNodeBiComAsv* bnodep = nodep->castNodeBiComAsv(); + if (!bnodep) return false; + if (!bnodep->lhsp()->castConst()) return false; + AstNodeBiComAsv* rnodep = bnodep->rhsp()->castNodeBiComAsv(); + if (!rnodep) return false; + if (rnodep->type() != bnodep->type()) return false; + if (rnodep->width() != bnodep->width()) return false; + if (rnodep->lhsp()->width() != bnodep->lhsp()->width()) return false; + if (!rnodep->lhsp()->castConst()) return false; + return true; + } + bool operandHugeShiftL(AstNodeBiop* nodep) { + return (nodep->rhsp()->castConst() + && nodep->rhsp()->castConst()->asInt() >= (uint32_t)(nodep->width())); + } + bool operandHugeShiftR(AstNodeBiop* nodep) { + return (nodep->rhsp()->castConst() + && nodep->rhsp()->castConst()->asInt() >= (uint32_t)(nodep->lhsp()->width())); + } + bool operandIsTwo(AstNode* nodep) { + return (nodep->castConst() + && nodep->width() <= VL_QUADSIZE + && nodep->castConst()->asQuad()==2); + } + bool operandIsTwostate(AstNode* nodep) { + return (nodep->castConst() + && !nodep->castConst()->num().isFourState()); + } + bool operandShiftOp(AstNodeBiop* nodep) { + if (!nodep->rhsp()->castConst()) return false; + AstNodeBiop* lhsp = nodep->lhsp()->castNodeBiop(); + if (!lhsp || !(lhsp->castAnd()||lhsp->castOr()||lhsp->castXor())) return false; + if (nodep->width()!=lhsp->width()) return false; + if (nodep->width()!=lhsp->lhsp()->width()) return false; + if (nodep->width()!=lhsp->rhsp()->width()) return false; + return true; + } + bool operandShiftShift(AstNodeBiop* nodep) { + // We could add a AND though. + AstNodeBiop* lhsp = nodep->lhsp()->castNodeBiop(); + if (!lhsp || !(lhsp->castShiftL()||lhsp->castShiftR())) return false; + // We can only get rid of a<>c or a<rhsp()->castConst() && lhsp->rhsp()->castConst())) return false; + if (nodep->width()!=lhsp->width()) return false; + if (nodep->width()!=lhsp->lhsp()->width()) return false; + return true; + } + bool operandWordOOB(AstWordSel* nodep) { + // V3Expand may make a arraysel that exceeds the bounds of the array + // It was an expression, then got constified. In reality, the WordSel + // must be wrapped in a Cond, that will be false. + return (nodep->rhsp()->castConst() + && nodep->fromp()->castNodeVarRef() + && !nodep->fromp()->castNodeVarRef()->lvalue() + && ((int)(nodep->rhsp()->castConst()->asInt()) + >= nodep->fromp()->castNodeVarRef()->varp()->widthWords())); + } + bool operandSelFull(AstSel* nodep) { + return (nodep->lsbp()->castConst() + && nodep->widthp()->castConst() + && nodep->lsbConst()==0 + && (int)nodep->widthConst()==nodep->fromp()->width() + && 1); + } + bool operandSelExtend(AstSel* nodep) { + // A pattern created by []'s after offsets have been removed + // SEL(EXTEND(any,width,...),(width-1),0) -> ... + // Since select's return unsigned, this is always an extend + AstExtend* extendp = nodep->fromp()->castExtend(); + return (!m_cpp + && extendp + && nodep->lsbp()->castConst() + && nodep->widthp()->castConst() + && nodep->lsbConst()==0 + && (int)nodep->widthConst()==extendp->lhsp()->width() + ); + } + + // Extraction checks + bool warnSelect(AstSel* nodep) { + AstNode* basefromp = AstArraySel::baseFromp(nodep->fromp()); + if (AstNodeVarRef* varrefp = basefromp->castNodeVarRef()) { + if (m_warn + && nodep->lsbp()->castConst() + && nodep->widthp()->castConst() + && (!varrefp->varp()->rangep() || varrefp->varp()->msb()) // else it's non-resolvable parameterized + && ( ( (nodep->msbConst() > varrefp->varp()->msb()) + || (nodep->lsbConst() > varrefp->varp()->msb())))) { + nodep->v3error("Selection index out of range: " + <msbConst()<<":"<lsbConst() + <<" outside "<varp()->msb()<<":0"); + } + } + return false; // Not a transform, so NOP + } + + static bool operandsSame(AstNode* node1p, AstNode* node2p) { + // For now we just detect constants & simple vars, though it could be more generic + if (node1p->castConst() && node2p->castConst()) { + // Match ignoring any X values + V3Number num (node1p->fileline(), 1); + num.opCaseEq(node1p->castConst()->num(), node2p->castConst()->num()); + return num.isNeqZero(); + } + else if (node1p->castVarRef() && node2p->castVarRef() + && node1p->sameTree(node2p)) { + // Same variable + return true; + } else { + return false; + } + } + bool ifSameAssign(AstNodeIf* nodep) { + AstNodeAssign* ifp = nodep->ifsp()->castNodeAssign(); + AstNodeAssign* elsep = nodep->elsesp()->castNodeAssign(); + if (!ifp || ifp->nextp()) return false; // Must be SINGLE statement + if (!elsep || elsep->nextp()) return false; + if (ifp->type() != elsep->type()) return false; // Can't mix an assigndly and an assign + AstVarRef* ifvarp = ifp->lhsp()->castVarRef(); + AstVarRef* elsevarp = elsep->lhsp()->castVarRef(); + if (!ifvarp || !elsevarp) return false; + if (ifvarp->isWide()) return false; // Would need temporaries, so not worth it + if (ifvarp->varp() != elsevarp->varp()) return false; + return true; + } + bool operandIfIf(AstNodeIf* nodep) { + if (nodep->elsesp()) return false; + AstNodeIf* lowerIfp = nodep->ifsp()->castNodeIf(); + if (!lowerIfp || lowerIfp->nextp()) return false; + if (nodep->type() != lowerIfp->type()) return false; + if (lowerIfp->elsesp()) return false; + return true; + } + + //---------------------------------------- + // Constant Replacement functions. + // These all take a node, delete its tree, and replaces it with a constant + + void replaceNum (AstNode* oldp, const V3Number& num) { + // Replace oldp node with a constant set to specified value + UASSERT (oldp, "Null old\n"); + if (oldp->castConst()) oldp->v3fatalSrc("Already constant??\n"); + AstNode* newp = new AstConst(oldp->fileline(), num); + newp->widthSignedFrom(oldp); + if (debug()>5) oldp->dumpTree(cout," const_old: "); + if (debug()>5) newp->dumpTree(cout," _new: "); + oldp->replaceWith(newp); + oldp->deleteTree(); oldp=NULL; + } + void replaceNum (AstNode* nodep, uint32_t val) { + V3Number num (nodep->fileline(), nodep->width(), val); + replaceNum(nodep, num); nodep=NULL; + } + void replaceNumSigned(AstNodeBiop* nodep, uint32_t val) { + // We allow both sides to be constant, as one may have come from parameter propagation, etc. + if (m_warn && !(nodep->lhsp()->castConst() && nodep->rhsp()->castConst())) { + nodep->v3warn(UNSIGNED,"Comparison is constant due to unsigned arithmetic\n"); + } + replaceNum(nodep, val); nodep=NULL; + } + void replaceNumLimited(AstNodeBiop* nodep, uint32_t val) { + // Avoids gcc warning about same + if (m_warn) nodep->v3warn(CMPCONST,"Comparison is constant due to limited range\n"); + replaceNum(nodep, val); nodep=NULL; + } + void replaceZero(AstNode* nodep) { + replaceNum(nodep, 0); nodep=NULL; + } + void replaceConst(AstNodeUniop* nodep) { + V3Number num (nodep->fileline(), nodep->width()); + nodep->numberOperate(num, nodep->lhsp()->castConst()->num()); + UINFO(4,"UNICONST -> "<fileline(), nodep->width()); + nodep->numberOperate(num, nodep->lhsp()->castConst()->num(), nodep->rhsp()->castConst()->num()); + UINFO(4,"BICONST -> "<fileline(), nodep->width()); + nodep->numberOperate(num, nodep->lhsp()->castConst()->num(), + nodep->rhsp()->castConst()->num(), + nodep->thsp()->castConst()->num()); + UINFO(4,"TRICONST -> "< CHILD(...) + childp->unlinkFrBackWithNext(); + childp->widthSignedFrom(nodep); + nodep->replaceWith(childp); + nodep->deleteTree(); nodep=NULL; + } + void replaceWLhs(AstNodeUniop* nodep) { + // Keep LHS, remove RHS + replaceWChild(nodep, nodep->lhsp()); + } + void replaceWLhs(AstNodeBiop* nodep) { + // Keep LHS, remove RHS + replaceWChild(nodep, nodep->lhsp()); + } + void replaceWRhs(AstNodeBiop* nodep) { + // Keep RHS, remove LHS + replaceWChild(nodep, nodep->rhsp()); + } + void replaceAsvConst (AstNodeBiop* nodep) { + // BIASV(CONSTa, BIASV(CONSTb, c)) -> BIASV( BIASV_CONSTED(a,b), c) + //nodep->dumpTree(cout, " repAsvConst_old: "); + AstNode* ap = nodep->lhsp(); + AstNodeBiop* rp = nodep->rhsp()->castNodeBiop(); + AstNode* bp = rp->lhsp(); + AstNode* cp = rp->rhsp(); + ap->unlinkFrBack(); + bp->unlinkFrBack(); + cp->unlinkFrBack(); + rp->unlinkFrBack(); + nodep->lhsp(rp); + nodep->rhsp(cp); + rp->lhsp(ap); + rp->rhsp(bp); + replaceConst(rp); + //nodep->dumpTree(cout, " repAsvConst_new: "); + } + void replaceExtend (AstNode* nodep, AstNode* arg0p) { + // -> EXTEND(nodep) + // like a AstExtend{$rhsp}, but we need to set the width correctly from base node + arg0p->unlinkFrBack(); + AstNode* newp = (nodep->castExtendS() + ? (new AstExtendS(nodep->fileline(), arg0p))->castNode() + : (new AstExtend (nodep->fileline(), arg0p))->castNode()); + newp->widthSignedFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL; + } + void replacePowShift (AstNodeBiop* nodep) { // Pow or PowS + UINFO(5,"POW(2,b)->SHIFTL(1,b) "<rhsp()->unlinkFrBack(); + AstShiftL* newp = new AstShiftL(nodep->fileline(), + new AstConst(nodep->fileline(), 1), + rhsp); + newp->widthSignedFrom(nodep); + newp->lhsp()->widthSignedFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL; + } + void replaceShiftOp (AstNodeBiop* nodep) { + UINFO(5,"SHIFT(AND(a,b),CONST)->AND(SHIFT(a,CONST),SHIFT(b,CONST)) "<unlinkFrBack(&handle); + AstNodeBiop* lhsp = nodep->lhsp()->castNodeBiop(); lhsp->unlinkFrBack(); + AstNode* shiftp = nodep->rhsp()->unlinkFrBack(); + AstNode* ap = lhsp->lhsp()->unlinkFrBack(); + AstNode* bp = lhsp->rhsp()->unlinkFrBack(); + AstNodeBiop* shift1p = nodep; + AstNodeBiop* shift2p = nodep->cloneTree(true)->castNodeBiop(); + shift1p->lhsp(ap); shift1p->rhsp(shiftp->cloneTree(true)); + shift2p->lhsp(bp); shift2p->rhsp(shiftp); + AstNodeBiop* newp = lhsp; + newp->lhsp(shift1p); newp->rhsp(shift2p); + handle.relink(newp); + newp->accept(*this); // Further reduce, either node may have more reductions. + } + void replaceShiftShift (AstNodeBiop* nodep) { + UINFO(4,"SHIFT(SHIFT(a,s1),s2)->SHIFT(a,ADD(s1,s2)) "<=9) nodep->dumpTree(cout, " repShiftShift_old: "); + AstNodeBiop* lhsp = nodep->lhsp()->castNodeBiop(); lhsp->unlinkFrBack(); + AstNode* ap = lhsp->lhsp()->unlinkFrBack(); + AstNode* shift1p = lhsp->rhsp()->unlinkFrBack(); + AstNode* shift2p = nodep->rhsp()->unlinkFrBack(); + if (nodep->type()==lhsp->type()) { + nodep->lhsp(ap); + nodep->rhsp(new AstAdd(nodep->fileline(), shift1p, shift2p)); + nodep->accept(*this); // Further reduce, either node may have more reductions. + } else { + // We know shift amounts are constant, but might be a mixed left/right shift + int shift1 = shift1p->castConst()->asInt(); if (lhsp->castShiftR()) shift1=-shift1; + int shift2 = shift2p->castConst()->asInt(); if (nodep->castShiftR()) shift2=-shift2; + int newshift = shift1+shift2; + AstNode* newp; + V3Number mask1 (nodep->fileline(), nodep->width()); + V3Number ones (nodep->fileline(), nodep->width()); + ones.opNot(mask1); + if (shift1<0) { + mask1.opShiftR(ones,V3Number(nodep->fileline(),VL_WORDSIZE,-shift1)); + } else { + mask1.opShiftL(ones,V3Number(nodep->fileline(),VL_WORDSIZE,shift1)); + } + V3Number mask (nodep->fileline(), nodep->width()); + if (shift2<0) { + mask.opShiftR(mask1,V3Number(nodep->fileline(),VL_WORDSIZE,-shift2)); + } else { + mask.opShiftL(mask1,V3Number(nodep->fileline(),VL_WORDSIZE,shift2)); + } + if (newshift<0) { + newp = new AstShiftR(nodep->fileline(), ap, + new AstConst(nodep->fileline(), -newshift)); + } else { + newp = new AstShiftL(nodep->fileline(), ap, + new AstConst(nodep->fileline(), newshift)); + } + newp->widthSignedFrom(nodep); + newp = new AstAnd (nodep->fileline(), + newp, + new AstConst (nodep->fileline(), mask)); + newp->widthSignedFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL; + //newp->dumpTree(cout, " repShiftShift_new: "); + newp->accept(*this); // Further reduce, either node may have more reductions. + } + lhsp->deleteTree(); lhsp=NULL; + } + + bool replaceAssignMultiSel(AstNodeAssign* nodep) { + // Multiple assignments to sequential bits can be concated + // ASSIGN(SEL(a),aq), ASSIGN(SEL(a+1),bq) -> ASSIGN(SEL(a:b),CONCAT(aq,bq) + // ie. assign var[2]=a, assign var[3]=b -> assign var[3:2]={b,a} + if (!m_modp) return false; // Skip if we're not const'ing an entire module (IE doing only one assign, etc) + AstSel* sel1p = nodep->lhsp()->castSel(); if (!sel1p) return false; + AstNodeAssign* nextp = nodep->nextp()->castNodeAssign(); if (!nextp) return false; + if (nodep->type() != nextp->type()) return false; + AstSel* sel2p = nextp->lhsp()->castSel(); if (!sel2p) return false; + AstVarRef* varref1p = sel1p->fromp()->castVarRef(); if (!varref1p) return false; + AstVarRef* varref2p = sel2p->fromp()->castVarRef(); if (!varref2p) return false; + if (varref1p->varp() != varref2p->varp()) return false; + AstConst* con1p = sel1p->lsbp()->castConst(); if (!con1p) return false; + AstConst* con2p = sel2p->lsbp()->castConst(); if (!con2p) return false; + // We need to make sure there's no self-references involved in either + // assignment. For speed, we only look 3 deep, then give up. + if (!varNotReferenced(nodep->rhsp(), varref1p->varp())) return false; + if (!varNotReferenced(nextp->rhsp(), varref2p->varp())) return false; + // Swap? + if (( con1p->asInt() != con2p->asInt() + sel2p->width()) + &&(con2p->asInt() != con1p->asInt() + sel1p->width())) return false; + bool lsbFirstAssign = (con1p->asInt() < con2p->asInt()); + // If the user already has nice 32-bit divisions, keep them to aid later subdivision + //if (VL_BITBIT_I(con1p->asInt()) == 0) return false; + UINFO(4,"replaceAssignMultiSel "<dumpTree(cout, "comb1: "); + //nextp->dumpTree(cout, "comb2: "); + AstNode* rhs1p = nodep->rhsp()->unlinkFrBack(); + AstNode* rhs2p = nextp->rhsp()->unlinkFrBack(); + AstNode* newp; + if (lsbFirstAssign) { + newp = nodep->cloneType (new AstSel(sel1p->fileline(), varref1p->unlinkFrBack(), + sel1p->lsbConst(), sel1p->width() + sel2p->width()), + new AstConcat(rhs1p->fileline(), rhs2p, rhs1p)); + } else { + newp = nodep->cloneType (new AstSel(sel1p->fileline(), varref1p->unlinkFrBack(), + sel2p->lsbConst(), sel1p->width() + sel2p->width()), + new AstConcat(rhs1p->fileline(), rhs1p, rhs2p)); + } + //pnewp->dumpTree(cout, "conew: "); + nodep->replaceWith(newp); nodep->deleteTree(); + nextp->unlinkFrBack()->deleteTree(); + return true; + } + + bool varNotReferenced(AstNode* nodep, AstVar* varp, bool level=0) { + // Return true if varp never referenced under node. + // Return false if referenced, or tree too deep to be worth it + if (!nodep) return true; + if (level>2) return false; + if (nodep->castNodeVarRef() && nodep->castNodeVarRef()->varp()==varp) return false; + return (varNotReferenced (nodep->nextp(),varp,level+1) + && varNotReferenced(nodep->op1p(),varp,level+1) + && varNotReferenced(nodep->op2p(),varp,level+1) + && varNotReferenced(nodep->op3p(),varp,level+1) + && varNotReferenced(nodep->op4p(),varp,level+1)); + } + + bool replaceNodeAssign(AstNodeAssign* nodep) { + if (nodep->lhsp()->castVarRef() + && nodep->rhsp()->castVarRef() + && nodep->lhsp()->sameTree(nodep->rhsp()) + && !nodep->castAssignDly()) { + // X = X. Quite pointless, though X <= X may override another earlier assignment + if (nodep->castAssignW()) { + nodep->v3error("Wire inputs its own output, creating circular logic (wire x=x)"); + return false; // Don't delete the assign, or V3Gate will freak out + } else { + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + return true; + } + } + else if (!m_cpp && nodep->lhsp()->castConcat()) { + UINFO(4," ASSI "<=9) nodep->dumpTree(cout," Ass_old: "); + // ASSIGN(CONCAT(lc1,lc2),rhs) -> ASSIGN(lc1,SEL(rhs,{size})), + // ASSIGN(lc2,SEL(newrhs,{size})) + // Unlink the stuff + AstNode* lc1p = nodep->lhsp()->castConcat()->lhsp()->unlinkFrBack(); + AstNode* lc2p = nodep->lhsp()->castConcat()->rhsp()->unlinkFrBack(); + AstNode* conp = nodep->lhsp()->castConcat()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* rhs2p = rhsp->cloneTree(false); + // Calc widths + int lsb2 = 0; + int msb2 = lsb2+lc2p->width()-1; + int lsb1 = msb2+1; + int msb1 = lsb1+lc1p->width()-1; + if (msb1!=(conp->width()-1)) nodep->v3fatalSrc("Width calc mismatch"); + // Form ranges + AstSel* sel1p = new AstSel(conp->fileline(), rhsp, lsb1, msb1-lsb1+1); + AstSel* sel2p = new AstSel(conp->fileline(), rhs2p, lsb2, msb2-lsb2+1); + sel1p->width(msb1-lsb1+1,msb1-lsb1+1); + sel2p->width(msb2-lsb2+1,msb2-lsb2+1); + // Make new assigns of same flavor as old one + //*** Not cloneTree; just one node. + AstNodeAssign* asn1p=nodep->cloneType(lc1p, sel1p)->castNodeAssign(); + AstNodeAssign* asn2p=nodep->cloneType(lc2p, sel2p)->castNodeAssign(); + asn1p->width(msb1-lsb1+1,msb1-lsb1+1); + asn2p->width(msb2-lsb2+1,msb2-lsb2+1); + nodep->addNextHere(asn1p); + nodep->addNextHere(asn2p); + if (debug()>=9) asn1p->dumpTree(cout," _new: "); + if (debug()>=9) asn2p->dumpTree(cout," _new: "); + // Cleanup + nodep->unlinkFrBack()->deleteTree(); + // Further reduce, either node may have more reductions. + return true; + } + else if (replaceAssignMultiSel(nodep)) { + return true; + } + return false; + } + + // Boolean replacements + bool operandBoolShift(AstNode* nodep) { + // boolean test of AND(const,SHIFTR(x,const)) -> test of AND(SHIFTL(x,const), x) + if (!nodep->castAnd()) return false; + if (!nodep->castAnd()->lhsp()->castConst()) return false; + if (!nodep->castAnd()->rhsp()->castShiftR()) return false; + AstShiftR* shiftp = nodep->castAnd()->rhsp()->castShiftR(); + if (!shiftp->rhsp()->castConst()) return false; + if ((uint32_t)(nodep->width()) <= shiftp->rhsp()->castConst()->asInt()) return false; + return true; + } + void replaceBoolShift(AstNode* nodep) { + if (debug()>=9) nodep->dumpTree(cout," bshft_old: "); + AstConst* andConstp = nodep->castAnd()->lhsp()->castConst(); + AstNode* fromp = nodep->castAnd()->rhsp()->castShiftR()->lhsp()->unlinkFrBack(); + AstConst* shiftConstp = nodep->castAnd()->rhsp()->castShiftR()->rhsp()->castConst(); + V3Number val (andConstp->fileline(), andConstp->width()); + val.opShiftL(andConstp->num(), shiftConstp->num()); + AstAnd* newp = new AstAnd(nodep->fileline(), + new AstConst(nodep->fileline(), val), + fromp); + newp->width(nodep->width(), nodep->width()); // widthMin no longer applicable + nodep->replaceWith(newp); + nodep->deleteTree(); nodep=NULL; + if (debug()>=9) newp->dumpTree(cout," _new: "); + } + + //---------------------------------------- + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + // Iterate modules backwards, in bottom-up order. That's faster + nodep->iterateChildrenBackwards(*this); + } + virtual void visit(AstModule* nodep, AstNUser*) { + m_modp = nodep; + nodep->iterateChildren(*this); + m_modp = NULL; + } + virtual void visit(AstCFunc* nodep, AstNUser*) { + // No ASSIGNW removals under funcs, we've long eliminated INITIALs + // (We should perhaps rename the assignw's to just assigns) + m_wremove = false; + nodep->iterateChildren(*this); + m_wremove = true; + } + virtual void visit(AstScope* nodep, AstNUser*) { + // No ASSIGNW removals under scope, we've long eliminated INITIALs + m_scopep = nodep; + m_wremove = false; + nodep->iterateChildren(*this); + m_wremove = true; + m_scopep = NULL; + } + + void swapSides(AstNodeBiCom* nodep) { + // COMMUNATIVE({a},CONST) -> COMMUNATIVE(CONST,{a}) + // This simplifies later optimizations + AstNode* lhsp = nodep->lhsp()->unlinkFrBackWithNext(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBackWithNext(); + nodep->lhsp(rhsp); + nodep->rhsp(lhsp); + nodep->accept(*this); // Again? + } + + int operandConcatMove(AstConcat* nodep) { + // CONCAT under concat (See moveConcat) + // Return value: true indicates to do it; 2 means move to LHS + AstConcat* abConcp = nodep->lhsp()->castConcat(); + AstConcat* bcConcp = nodep->rhsp()->castConcat(); + if (!abConcp && !bcConcp) return 0; + if (bcConcp) { + AstNode* ap = nodep->lhsp(); + AstNode* bp = bcConcp->lhsp(); + // If a+b == 32,64,96 etc, then we want to have a+b together on LHS + if (VL_BITBIT_I(ap->width()+bp->width())==0) return 2; // Transform 2: to abConc + } + else { //abConcp + // Unless lhs is already 32 bits due to above, reorder it + if (VL_BITBIT_I(nodep->lhsp()->width())!=0) return 1; // Transform 1: to bcConc + } + return 0; // ok + } + void moveConcat(AstConcat* nodep) { + // 1: CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b}, {c})) + // or 2: CONCAT({a}, CONCAT({b},{c})) -> CONCAT(CONCAT({a},{b}),{c}) + // Because the lhs of a concat needs a shift but the rhs doesn't, + // putting additional CONCATs on the RHS leads to fewer assembler operations. + // However, we'll end up with lots of wide moves if we make huge trees + // like that, so on 32 bit boundaries, we'll do the opposite form. + UINFO(4,"Move concat: "<1) { + AstNode* ap = nodep->lhsp()->unlinkFrBack(); + AstConcat* bcConcp = nodep->rhsp()->castConcat(); bcConcp->unlinkFrBack(); + AstNode* bp = bcConcp->lhsp()->unlinkFrBack(); + AstNode* cp = bcConcp->rhsp()->unlinkFrBack(); + AstConcat* abConcp = new AstConcat(bcConcp->fileline(), + ap, bp); + nodep->lhsp(abConcp); + nodep->rhsp(cp); + // If bp was a concat, then we have this exact same form again! + // Recurse rather then calling node->iterate to prevent 2^n recursion! + if (operandConcatMove(abConcp)) moveConcat(abConcp); + } else { + AstConcat* abConcp = nodep->lhsp()->castConcat(); abConcp->unlinkFrBack(); + AstNode* ap = abConcp->lhsp()->unlinkFrBack(); + AstNode* bp = abConcp->rhsp()->unlinkFrBack(); + AstNode* cp = nodep->rhsp()->unlinkFrBack(); + AstConcat* bcConcp = new AstConcat(abConcp->fileline(), + bp, cp); + nodep->lhsp(ap); + nodep->rhsp(bcConcp); + if (operandConcatMove(bcConcp)) moveConcat(bcConcp); + } + } + + // Special cases + virtual void visit(AstConst* nodep, AstNUser*) {} // Already constant + + virtual void visit(AstCell* nodep, AstNUser*) { + if (m_params) { + nodep->paramsp()->iterateAndNext(*this); + } else { + nodep->iterateChildren(*this); + } + } + virtual void visit(AstPin* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + + void replaceSelSel(AstSel* nodep) { + // SEL(SEL({x},a,b),c,d) => SEL({x},a+c,d) + AstSel* belowp = nodep->fromp()->castSel(); + AstNode* fromp = belowp->fromp()->unlinkFrBack(); + AstNode* widthp = nodep->widthp()->unlinkFrBack(); + AstNode* lsb1p = nodep->lsbp()->unlinkFrBack(); + AstNode* lsb2p = belowp->lsbp()->unlinkFrBack(); + // Eliminate lower range + UINFO(4,"Elim Lower range: "<castConst() && lsb2p->castConst()) { + newlsbp = new AstConst(lsb1p->fileline(), + lsb1p->castConst()->asInt() + lsb2p->castConst()->asInt()); + } else { + // Width is important, we need the width of the fromp's + // expression, not the potentially smaller lsb1p's width + newlsbp = new AstAdd(lsb1p->fileline(), + lsb2p, new AstExtend(lsb1p->fileline(), lsb1p)); + newlsbp->widthFrom(lsb2p); // Unsigned + newlsbp->castAdd()->rhsp()->widthFrom(lsb2p); // Unsigned + } + AstSel* newp = new AstSel(nodep->fileline(), + fromp, + newlsbp, + widthp); + nodep->replaceWith(newp); + nodep->deleteTree(); nodep=NULL; + } + + void replaceSelConcat(AstSel* nodep) { + // SEL(CONCAT(a,b),c,d) => SEL(a or b, . .) + AstConcat* conp = nodep->fromp()->castConcat(); + AstNode* conLhsp = conp->lhsp(); + AstNode* conRhsp = conp->rhsp(); + if ((int)nodep->lsbConst() >= conRhsp->width()) { + conLhsp->unlinkFrBack(); + AstSel* newp = new AstSel(nodep->fileline(), + conLhsp, + nodep->lsbConst() - conRhsp->width(), + nodep->widthConst()); + nodep->replaceWith(newp); + } + else if ((int)nodep->msbConst() < conRhsp->width()) { + conRhsp->unlinkFrBack(); + AstSel* newp = new AstSel(nodep->fileline(), + conRhsp, + nodep->lsbConst(), + nodep->widthConst()); + nodep->replaceWith(newp); + } + else { + // Yuk, split between the two + conRhsp->unlinkFrBack(); + conLhsp->unlinkFrBack(); + AstConcat* newp = new AstConcat + (nodep->fileline(), + new AstSel(nodep->fileline(), + conLhsp, + 0, + nodep->msbConst() - conRhsp->width() + 1), + new AstSel(nodep->fileline(), + conRhsp, + nodep->lsbConst(), + conRhsp->width()-nodep->lsbConst())); + nodep->replaceWith(newp); + } + nodep->deleteTree(); nodep=NULL; + } + + void replaceSelReplicate(AstSel* nodep) { + // SEL(REPLICATE(a,b),1,bit) => SEL(a,1,bit) + AstReplicate* repp = nodep->fromp()->castReplicate(); + AstNode* fromp = repp->lhsp()->unlinkFrBack(); + AstConst* lsbp = nodep->lsbp()->castConst(); + AstNode* widthp = nodep->widthp()->unlinkFrBack(); + AstSel* newp = new AstSel(nodep->fileline(), + fromp, + new AstConst(lsbp->fileline(), lsbp->asInt() % fromp->width()), + widthp); + newp->widthSignedFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL; + } + + virtual void visit(AstVarRef* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (!nodep->varp()) nodep->v3fatalSrc("Not linked"); + bool did=false; + if (!m_cpp && nodep->varp()->hasSimpleInit()) { + //if (debug()) nodep->varp()->initp()->dumpTree(cout," visitvaref: "); + nodep->varp()->initp()->iterateAndNext(*this); + if (operandConst(nodep->varp()->initp()) + && !nodep->lvalue() + && ((v3Global.opt.oConst() && !m_params) // Can reduce constant wires into equations + || nodep->varp()->isParam())) { + AstConst* constp = nodep->varp()->initp()->castConst(); + const V3Number& num = constp->num(); + //UINFO(2,"constVisit "<<(void*)constp<<" "<v3error("Expecting expression to be constant, but variable isn't const: "<varp()->prettyName()); + } + } + virtual void visit(AstAttrOf* nodep, AstNUser*) { + // Don't iterate children, don't want to loose VarRef. + if (nodep->attrType()==AstAttrType::SCOPE_TEXT) { + } else if (nodep->attrType()==AstAttrType::BITS) { + if (!nodep->fromp() || !nodep->fromp()->widthMin()) nodep->v3fatalSrc("Unsized expression"); + V3Number num (nodep->fileline(), 32, nodep->fromp()->widthMin()); + replaceNum(nodep, num); nodep=NULL; + } else { + if (!nodep->fromp()->castNodeVarRef()) nodep->v3fatalSrc("Not linked"); + AstVar* varp = nodep->fromp()->castNodeVarRef()->varp(); + if (!varp) nodep->v3fatalSrc("Not linked"); + if (nodep->attrType()==AstAttrType::RANGE_LSB) { + if (!varp->rangep()) nodep->v3fatalSrc("RANGE_LSB on vec w/o range\n"); + if (operandConst(varp->rangep()->lsbp())) { + V3Number num (nodep->fileline(), 32, varp->lsb()); + replaceNum(nodep, num); nodep=NULL; + } + } else if (nodep->attrType()==AstAttrType::ARRAY_LSB) { + AstRange* arrayp=varp->arrayp(nodep->dimension()); + if (!arrayp) nodep->v3fatalSrc("ARRAY_LSB on vec w/o range or right # dimensions\n"); + if (operandConst(arrayp->lsbp())) { + V3Number num (nodep->fileline(), 32, arrayp->lsbConst()); + replaceNum(nodep, num); nodep=NULL; + } + } else nodep->v3fatalSrc("Missing ATTR type case\n"); + } + } + virtual void visit(AstSenItem* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (nodep->sensp()->castConst() + || (nodep->varrefp() && nodep->varrefp()->varp()->isParam())) { + // Constants in sensitivity lists may be removed (we'll simplify later) + AstSenItem* newp = new AstSenItem(nodep->fileline(), AstSenItem::Never()); + nodep->replaceWith(newp); + nodep->deleteTree(); nodep=NULL; + } else if (nodep->sensp()->castNot()) { + // V3Gate may propagate NOTs into clocks... Just deal with it + AstNode* sensp = nodep->sensp(); + AstNode* lastSensp = sensp; + bool invert = false; + while (lastSensp->castNot()) { + lastSensp = lastSensp->castNot()->lhsp(); + invert = !invert; + } + UINFO(8,"senItem(NOT...) "<edgeType( nodep->edgeType().invert() ); + AstVarRef* senvarp = lastSensp->unlinkFrBack()->castVarRef(); + if (!senvarp) sensp->v3fatalSrc("Non-varref sensitivity variable"); + sensp->replaceWith(senvarp); + sensp->deleteTree(); sensp=NULL; + } else { + if (nodep->hasVar() && !nodep->varrefp()) nodep->v3fatalSrc("Null sensitivity variable"); + } + } + + //----- + // Zero elimination + virtual void visit(AstNodeAssign* nodep, AstNUser*) { + nodep->iterateChildren(*this); + replaceNodeAssign(nodep); + } + virtual void visit(AstAssignAlias* nodep, AstNUser*) { + // Don't perform any optimizations, keep the alias around + } + virtual void visit(AstAssignW* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (replaceNodeAssign(nodep)) return; + AstNodeVarRef* varrefp = nodep->lhsp()->castVarRef(); // Not VarXRef, as different refs may set different values to each hierarchy + if (m_wremove + && m_modp && operandConst(nodep->rhsp()) + && !nodep->rhsp()->castConst()->num().isFourState() + && varrefp // Don't do messes with BITREFs/ARRAYREFs + && !varrefp->varp()->initp() // Not already constified + && !varrefp->varScopep() // Not scoped (or each scope may have different initial value) + && !m_params) { + // ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) ) + UINFO(4,"constAssignW "<rhsp()->unlinkFrBack(); + varrefp->unlinkFrBack(); + AstInitial* newinitp + = new AstInitial(nodep->fileline(), + new AstAssign(nodep->fileline(), + varrefp, exprp)); + m_modp->addStmtp(newinitp); + nodep->unlinkFrBack(); nodep=NULL; + // Set the initial value right in the variable so we can constant propagate + AstNode* initvaluep = exprp->cloneTree(false); + varrefp->varp()->initp(initvaluep); + } + } + + virtual void visit(AstNodeIf* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (AstConst* constp = nodep->condp()->castConst()) { + AstNode* keepp = NULL; + if (constp->isZero()) { + UINFO(4,"IF(0,{any},{x}) => {x}: "<elsesp(); + } else { + UINFO(4,"IF(!0,{x},{any}) => {x}: "<ifsp(); + } + if (keepp) { + keepp->unlinkFrBackWithNext(); + nodep->replaceWith(keepp); + } else { + nodep->unlinkFrBack(); + } + nodep->deleteTree(); nodep=NULL; + } + else if (!nodep->ifsp() && !nodep->elsesp()) { + // Empty block, remove it + // Note if we support more C++ then there might be side effects in the condition itself + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } + else if (!nodep->ifsp()) { + UINFO(4,"IF({x}) NULL {...} => IF(NOT{x}}: "<condp(); + AstNode* elsesp = nodep->elsesp(); + condp->unlinkFrBackWithNext(); + elsesp->unlinkFrBackWithNext(); + nodep->condp(new AstLogNot(condp->fileline(), condp)); // LogNot, as C++ optimization also possible + nodep->addIfsp(elsesp); + } + else if (((nodep->condp()->castNot() && nodep->condp()->width()==1) + || (nodep->condp()->castLogNot())) + && nodep->ifsp() && nodep->elsesp()) { + UINFO(4,"IF(NOT {x}) => IF(x) swapped if/else"<condp()->castNot()->lhsp()->unlinkFrBackWithNext(); + AstNode* ifsp = nodep->ifsp()->unlinkFrBackWithNext(); + AstNode* elsesp = nodep->elsesp()->unlinkFrBackWithNext(); + AstIf* ifp = new AstIf(nodep->fileline(), condp, elsesp, ifsp); + ifp->branchPred(nodep->branchPred().invert()); + nodep->replaceWith(ifp); + nodep->deleteTree(); nodep=NULL; + } + else if (ifSameAssign(nodep)) { + UINFO(4,"IF({a}) ASSIGN({b},{c}) else ASSIGN({b},{d}) => ASSIGN({b}, {a}?{c}:{d})"<ifsp()->castNodeAssign(); + AstNodeAssign* elsep = nodep->elsesp()->castNodeAssign(); + ifp->unlinkFrBack(); + AstNode* condp = nodep->condp()->unlinkFrBack(); + AstNode* truep = ifp->rhsp()->unlinkFrBack(); + AstNode* falsep = elsep->rhsp()->unlinkFrBack(); + ifp->rhsp(new AstCond(truep->fileline(), + condp, truep, falsep)); + nodep->replaceWith(ifp); + nodep->deleteTree(); nodep=NULL; + } + else if (0 // Disabled, as vpm assertions are faster without due to short-circuiting + && operandIfIf(nodep)) { + UINFO(0,"IF({a}) IF({b}) => IF({a} && {b})"<ifsp()->castNodeIf(); + AstNode* condp = nodep->condp()->unlinkFrBack(); + AstNode* lowerIfsp = lowerIfp->ifsp()->unlinkFrBackWithNext(); + AstNode* lowerCondp = lowerIfp->condp()->unlinkFrBackWithNext(); + nodep->condp(new AstLogAnd(lowerIfp->fileline(), + condp, lowerCondp)); + lowerIfp->replaceWith(lowerIfsp); + lowerIfp->deleteTree(); lowerIfp=NULL; + } + else if (operandBoolShift(nodep->condp())) { + replaceBoolShift(nodep->condp()); + } + } + + virtual void visit(AstWhile* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (nodep->condp()->isZero()) { + UINFO(4,"WHILE(0) => nop "<unlinkFrBack(); + nodep->deleteTree(); nodep=NULL; + } + else if (operandBoolShift(nodep->condp())) { + replaceBoolShift(nodep->condp()); + } + } + + //----- + // Below lines are magic expressions processed by astgen + // "AstNODETYPE { # bracket not paren + // $accessor_name, ... + // # ,, gets replaced with a , rather then && + // }" # bracket not paren + // ,"function to call" + // or ,"AstREPLACEMENT_TYPE{ $accessor }" + + // Lint Checks + // v--- *1* These ops are always first, as we warn before replacing + // v--- *V* This op is a verilog op, ignore when in m_cpp mode + TREEOP1("AstSel{warnSelect(nodep)}", "NEVER"); + // Generic constants on both side. Do this first to avoid other replacements + TREEOP("AstNodeBiop {$lhsp.castConst, $rhsp.castConst}", "replaceConst(nodep)"); + TREEOP("AstNodeUniop{$lhsp.castConst}", "replaceConst(nodep)"); + // Zero on one side or the other + TREEOP("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOP("AstAnd {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP("AstLogAnd{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP("AstLogOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOP("AstDiv {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP("AstDivS {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP("AstMul {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP("AstMulS {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP("AstPow {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP("AstPowS {$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP("AstOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOP("AstShiftL{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP("AstShiftR{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP("AstShiftRS{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOP("AstXor {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOP("AstSub {$lhsp.isZero, $rhsp}", "AstUnaryMin{$rhsp}"); + TREEOP("AstAdd {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP("AstAnd {$lhsp, $rhsp.isZero}", "replaceZero(nodep)"); + TREEOP("AstLogAnd{$lhsp, $rhsp.isZero}", "replaceZero(nodep)"); + TREEOP("AstLogOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP("AstMul {$lhsp, $rhsp.isZero}", "replaceZero(nodep)"); + TREEOP("AstMulS {$lhsp, $rhsp.isZero}", "replaceZero(nodep)"); + TREEOP("AstOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP("AstShiftL{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP("AstShiftR{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP("AstShiftRS{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP("AstSub {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP("AstXor {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + // Non-zero on one side or the other + TREEOP("AstAnd {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)"); + TREEOP("AstLogAnd{$lhsp.isNeqZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOP("AstOr {$lhsp.isAllOnes, $rhsp}", "replaceWLhs(nodep)"); //->allOnes + TREEOP("AstLogOr {$lhsp.isNeqZero, $rhsp}", "replaceNum(nodep,1)"); + TREEOP("AstAnd {$lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)"); + TREEOP("AstLogAnd{$lhsp, $rhsp.isNeqZero}", "replaceWLhs(nodep)"); + TREEOP("AstOr {$lhsp, $rhsp.isAllOnes}", "replaceWRhs(nodep)"); //->allOnes + TREEOP("AstLogOr {$lhsp, $rhsp.isNeqZero}", "replaceNum(nodep,1)"); + TREEOP("AstXor {$lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}"); + TREEOP("AstPow {operandIsTwo($lhsp), $rhsp}","replacePowShift(nodep)"); // 2**a == 1<castNot(), $expr1p, $expr2p}", "AstCond{$condp->op1p(), $expr2p, $expr1p}"); + TREEOP("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isAllOnes, $expr2p}", "AstOr {$condp, $expr2p}"); // a?1:b == a|b + TREEOP("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isZero}", "AstAnd{$condp, $expr1p}"); // a?b:0 == a&b + TREEOP("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isAllOnes}", "AstOr {AstNot{$condp}, $expr1p}"); // a?b:1 == ~a|b + TREEOP("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isZero, $expr2p}", "AstAnd{AstNot{$condp}, $expr2p}"); // a?0:b == ~a&b + TREEOP("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())"); + // Prefer constants on left, since that often needs a shift, it lets constant red remove the shift + TREEOP("AstNodeBiCom{$lhsp, $rhsp.castConst}", "swapSides(nodep)"); + TREEOP("AstNodeBiComAsv{operandAsvConst(nodep)}", "replaceAsvConst(nodep)"); + // v--- *1* as These ops are always first, as we warn before replacing + TREEOP1("AstLt {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,0)"); + TREEOP1("AstGte {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,1)"); + TREEOP1("AstGt {$lhsp.isZero, $rhsp}", "replaceNumSigned(nodep,0)"); + TREEOP1("AstLte {$lhsp.isZero, $rhsp}", "replaceNumSigned(nodep,1)"); + TREEOP1("AstGt {$lhsp, $rhsp.isAllOnes, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,0)"); + TREEOP1("AstLte {$lhsp, $rhsp.isAllOnes, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,1)"); + TREEOP1("AstLt {$lhsp.isAllOnes, $rhsp, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,0)"); + TREEOP1("AstGte {$lhsp.isAllOnes, $rhsp, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,1)"); + // Two level bubble pushing + TREEOP ("AstNot {$lhsp.castNot, $lhsp->width()==$lhsp->castNot()->lhsp()->width()}", "replaceWChild(nodep, $lhsp->op1p())"); // NOT(NOT(x))->x + TREEOP ("AstLogNot{$lhsp.castLogNot}", "replaceWChild(nodep, $lhsp->op1p())"); // NOT(NOT(x))->x + TREEOPV("AstNot {$lhsp.castEqCase, $lhsp.width1}","AstNeqCase{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castEqCase}", "AstNeqCase{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castNeqCase, $lhsp.width1}","AstEqCase {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castNeqCase}", "AstEqCase {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castEq, $lhsp.width1}", "AstNeq {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castEq}", "AstNeq {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castNeq, $lhsp.width1}", "AstEq {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castNeq}", "AstEq {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castLt, $lhsp.width1}", "AstGte {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castLt}", "AstGte {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castLtS, $lhsp.width1}", "AstGteS{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castLtS}", "AstGteS{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castLte, $lhsp.width1}", "AstGt {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castLte}", "AstGt {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castLteS, $lhsp.width1}", "AstGtS {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castLteS}", "AstGtS {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castGt, $lhsp.width1}", "AstLte {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castGt}", "AstLte {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castGtS, $lhsp.width1}", "AstLteS{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castGtS}", "AstLteS{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castGte, $lhsp.width1}", "AstLt {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castGte}", "AstLt {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castGteS, $lhsp.width1}", "AstLtS {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castGteS}", "AstLtS {$lhsp->op1p(),$lhsp->op2p()}"); + // Not common, but avoids compiler warnings about over shifting + TREEOP ("AstShiftL{operandHugeShiftL(nodep)}", "replaceZero(nodep)"); + TREEOP ("AstShiftR{operandHugeShiftR(nodep)}", "replaceZero(nodep)"); + TREEOP ("AstShiftL{operandShiftOp(nodep)}", "replaceShiftOp(nodep)"); + TREEOP ("AstShiftR{operandShiftOp(nodep)}", "replaceShiftOp(nodep)"); + TREEOP ("AstShiftL{operandShiftShift(nodep)}", "replaceShiftShift(nodep)"); + TREEOP ("AstShiftR{operandShiftShift(nodep)}", "replaceShiftShift(nodep)"); + TREEOP ("AstWordSel{operandWordOOB(nodep)}", "replaceZero(nodep)"); + ///=== Verilog operators + // Comparison against 1'b0/1'b1; must be careful about widths. + // These use Not, so must be Verilog only + TREEOPV("AstEq {$rhsp.width1, $lhsp.isZero, $rhsp}", "AstNot{$rhsp}"); + TREEOPV("AstEq {$lhsp.width1, $lhsp, $rhsp.isZero}", "AstNot{$lhsp}"); + TREEOPV("AstEq {$rhsp.width1, $lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)"); + TREEOPV("AstEq {$lhsp.width1, $lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)"); + TREEOPV("AstNeq {$rhsp.width1, $lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOPV("AstNeq {$lhsp.width1, $lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOPV("AstNeq {$rhsp.width1, $lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}"); + TREEOPV("AstNeq {$lhsp.width1, $lhsp, $rhsp.isAllOnes}", "AstNot{$lhsp}"); + TREEOPV("AstLt {$rhsp.width1, $lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); // Because not signed #s + TREEOPV("AstGt {$lhsp.width1, $lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); // Because not signed #s + // Useful for CONDs added around ARRAYSEL's in V3Case step + TREEOPV("AstLte {$lhsp->width()==$rhsp->width(), $rhsp.isAllOnes}", "replaceNum(nodep,1)"); + // Simplify reduction operators + // This also gets &{...,0,....} => const 0 (Common for unused_ok signals) + TREEOPV("AstRedXnor{$lhsp}", "AstNot{AstRedXor{$lhsp}}"); // Just eliminate XNOR's + TREEOPV("AstRedAnd{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)"); + TREEOPV("AstRedOr {$lhsp, $lhsp.width1}", "replaceWLhs(nodep)"); + TREEOPV("AstRedXor{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)"); + TREEOPV("AstRedAnd{$lhsp->castConcat()}", "AstAnd{AstRedAnd{$lhsp->castConcat()->lhsp()}, AstRedAnd{$lhsp->castConcat()->rhsp()}}"); // &{a,b} => {&a}&{&b} + TREEOPV("AstRedOr {$lhsp->castConcat()}", "AstOr {AstRedOr {$lhsp->castConcat()->lhsp()}, AstRedOr {$lhsp->castConcat()->rhsp()}}"); // |{a,b} => {|a}|{|b} + TREEOPV("AstRedXor{$lhsp->castConcat()}", "AstXor{AstRedXor{$lhsp->castConcat()->lhsp()}, AstRedXor{$lhsp->castConcat()->rhsp()}}"); // ^{a,b} => {^a}^{^b} + TREEOPV("AstRedAnd{$lhsp->castExtend(), $lhsp->width()>$lhsp->castExtend()->lhsp()->width()}", "replaceZero(nodep)"); // &{0,...} => 0 Prevents compiler limited range error + TREEOPV("AstRedOr {$lhsp->castExtend()}", "AstRedOr {$lhsp->castExtend()->lhsp()}"); + TREEOPV("AstRedXor{$lhsp->castExtend()}", "AstRedXor{$lhsp->castExtend()->lhsp()}"); + TREEOPV("AstOneHot{$lhsp.width1}", "replaceWLhs(nodep)"); + TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)"); + // Binary AND/OR is faster then logical and/or (usually) + TREEOPV("AstLogAnd{$lhsp.width1, $rhsp.width1}", "AstAnd{$lhsp,$rhsp}"); + TREEOPV("AstLogOr {$lhsp.width1, $rhsp.width1}", "AstOr{$lhsp,$rhsp}"); + TREEOPV("AstLogNot{$lhsp.width1}", "AstNot{$lhsp}"); + // CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b},{c})) + // CONCAT({const},CONCAT({const},{c})) -> CONCAT((constifiedCONC{const|const},{c})) + TREEOPV("AstConcat{operandConcatMove(nodep)}", "moveConcat(nodep)"); + TREEOPV("AstConcat{$lhsp.isZero, $rhsp}", "replaceExtend(nodep, nodep->rhsp())"); + // Note can't simplify a extend{extends}, extends{extend}, as the sign bits end up in the wrong places + TREEOPV("AstExtend {$lhsp.castExtend}", "replaceExtend(nodep, nodep->lhsp()->castExtend()->lhsp())"); + TREEOPV("AstExtendS{$lhsp.castExtendS}", "replaceExtend(nodep, nodep->lhsp()->castExtendS()->lhsp())"); + TREEOPV("AstReplicate{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs + // Next rule because AUTOINST puts the width of bits in + // to pins, even when the widths are exactly the same across the hierarchy. + TREEOPV("AstSel{operandSelExtend(nodep)}", "replaceWChild(nodep, nodep->fromp()->castExtend()->lhsp())"); + TREEOPV("AstSel{operandSelFull(nodep)}", "replaceWChild(nodep, nodep->fromp())"); + TREEOPV("AstSel{$fromp.castSel}", "replaceSelSel(nodep)"); + TREEOPV("AstSel{$fromp.castConst, $lsbp.castConst, $widthp.castConst, }", "replaceConst(nodep)"); + TREEOPV("AstSel{$fromp.castConcat, $lsbp.castConst, $widthp.castConst, }", "replaceSelConcat(nodep)"); + TREEOPV("AstSel{$fromp.castReplicate, $lsbp.castConst, $widthp.isOne, }", "replaceSelReplicate(nodep)"); + // Conversions + TREEOPV("AstLogIf {$lhsp, $rhsp}", "AstLogOr{AstLogNot{$lhsp},$rhsp}"); + TREEOPV("AstLogIff{$lhsp, $rhsp}", "AstLogNot{AstXor{$lhsp,$rhsp}}"); + + // Possible futures: + // (a?(b?y:x):y) -> (a&&!b)?x:y + // (a?(b?x:y):y) -> (a&&b)?x:y + // (a?x:(b?x:y)) -> (a||b)?x:y + // (a?x:(b?y:x)) -> (a||!b)?x:y + + // Note we can't convert EqCase/NeqCase to Eq/Neq here because that would break 3'b1x1==3'b101 + + //----- + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + if (m_required) { + nodep->v3error("Expecting expression to be constant, but can't convert a " + <typeName()<<" to constant."); + } else { + // Calculate the width of this operation + if (m_params && !nodep->width()) { + V3Width::widthParams(nodep); + V3Signed::signedParams(nodep); + } + nodep->iterateChildren(*this); + } + } + +public: + // CONSTUCTORS + ConstVisitor(bool params, bool required, bool warn, bool cpp) { + m_params = params; m_required = required; m_warn=warn; m_cpp=cpp; + m_wremove = true; + m_modp = NULL; + m_scopep = NULL; + } + virtual ~ConstVisitor() {} + void main(AstNode* nodep) { + // Operate starting at a random place + nodep->accept(*this); + } +}; + +//###################################################################### +// Const class functions + +void V3Const::constifyParam(AstNode* nodep) { + //if (debug()>0) nodep->dumpTree(cout," forceConPRE : "); + if (!nodep->width()) { + V3Width::widthParams(nodep); + V3Signed::signedParams(nodep); + } + ConstVisitor visitor (true,true,false,false); + visitor.main(nodep); + // Because we do edits, nodep links may get trashed and core dump this. + //if (debug()>0) nodep->dumpTree(cout," forceConDONE: "); +} + +void V3Const::constifyAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include + +#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 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: "<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: "<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: "<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: "<bodysp()->iterateAndNext(*this); + if (m_checkBlock) { // if the case body didn't disable it + UINFO(4," COVER: "<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: "<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: "<pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) { + // Skip all NEXT nodes under this block, and skip this if/case branch + UINFO(4," OFF: "<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__<<": "< +#include +#include +#include + +#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 m_varsp; // List of all encountered to avoid another loop through three + vector 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 "<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::iterator it = m_vscsp.begin(); it!=m_vscsp.end(); ++it) { + if ((*it)->user() == 0 && canElim((*it)->varp())) { + UINFO(4," Dead "<<(*it)<unlinkFrBack()->deleteTree(); (*it)=NULL; + } + } + for (vector::iterator it = m_varsp.begin(); it!=m_varsp.end(); ++it) { + if ((*it)->user() == 0 && canElim((*it))) { + UINFO(4," Dead "<<(*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__<<": "< 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 +#include +#include +#include +#include +#include + +#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,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 "<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: "<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: "< 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 dimreadps; // Read value for each dimension of assignment + for (unsigned dimension=0; dimensioncastConst()) { // 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 "<varScopep()->user4p()->castNode()->castAlwaysPost(); + if (!finalp) { + finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/); + UINFO(9," Created "<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 "<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 "<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: "<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: "<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: "<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 "<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__<<": "< +#include +#include +#include + +#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 "<=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 "<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__<<": "< +#include +#include +#include + +#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 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 "<name()<name()<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 "<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 "<argTypes()<<" und "<argTypes()<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(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," "<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__<<": "< +#include +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3EmitC.h" +#include "V3EmitCBase.h" + +#define VL_VALUE_STRING_MAX_WIDTH 1024 // We use a static char array in VL_VALUE_STRING + +//###################################################################### +// Coverage ID tracking + +class EmitCoverIds { + // TYPES + typedef map CoverIdMap; + // MEMBERS + CoverIdMap m_coverIds; // Remapping of coverage IDs to per-module value + int m_coverIdsSize; // Size of m_coverIds (to avoid O(n^2) insert) + // METHODS +public: + void clear() { + m_coverIds.clear(); + m_coverIdsSize = 0; + } + int size() const { return m_coverIdsSize; } + int remap(AstCoverDecl* declp) { + // Make cover ID for this point, unique on this module + CoverIdMap::iterator it = m_coverIds.find(declp); + if (it == m_coverIds.end()) { + m_coverIds.insert(make_pair(declp,m_coverIdsSize++)); // Remapping of coverage IDs to per-module value + return m_coverIdsSize-1; + } else { + return it->second; + } + } + EmitCoverIds() : m_coverIdsSize(0) {} + ~EmitCoverIds() {} +}; + +//###################################################################### +// Emit statements and math operators + +class EmitCStmts : public EmitCBaseVisitor { +private: + bool m_suppressSemi; + AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting + vector m_ctorVarsVec; // All variables in constructor order +public: + EmitCoverIds m_coverIds; // Coverage ID remapping +public: + //int debug() { return 9; } + + // METHODS + void displayEmit(AstDisplay* nodep); + string displayFormat(AstNode* widthNode, string in, + char fmtLetter, bool padZero, bool reallyString); + void displayArg(AstDisplay* dispp, AstNode** elistp, string fmt, char fmtLetter); + + void emitVarDecl(AstVar* nodep, const string& prefixIfImp); + typedef enum {EVL_IO, EVL_SIG, EVL_TEMP, EVL_ALL} EisWhich; + void emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp); + void emitVarCtors(); + bool emitSimpleOk(AstNodeMath* nodep); + void emitIQW(AstNode* nodep) { + puts (nodep->isWide()?"W":(nodep->isQuad()?"Q":"I")); + } + void emitOpName(AstNode* nodep, const string& name); + + string cFuncArgs(AstCFunc* nodep) { + // Return argument list for given C function + string args = nodep->argTypes(); + if (args=="") { + // Might be a user function with argument list. + for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) { + if (AstVar* portp = stmtp->castVar()) { + if (portp->isIO() && !portp->isFuncReturn()) { + if (args != "") args+= ", "; + args += portp->cType(); + if (portp->isOutput()) args += "&"; + args += " "+portp->name(); + } + } + } + } + return args; + } + + // VISITORS + virtual void visit(AstNodeAssign* nodep, AstNUser*) { + bool paren = true; bool decind = false; + if (AstSel* selp=nodep->lhsp()->castSel()) { + if (selp->widthMin()==1) { + putbs("VL_ASSIGNBIT_"); + emitIQW(selp->fromp()); + if (nodep->rhsp()->isAllOnesV()) { + putbs("O("); + } else { + putbs("I("); + } + puts(cvtToStr(nodep->widthMin())+","); + selp->lsbp()->iterateAndNext(*this); puts(", "); + selp->fromp()->iterateAndNext(*this); puts(", "); + } else { + putbs("VL_ASSIGNSEL_"); + emitIQW (selp->fromp()); + putbs("II"); + emitIQW(nodep->rhsp()); + puts("("); + puts(cvtToStr(nodep->widthMin())+","); + selp->lsbp()->iterateAndNext(*this); puts(", "); + selp->fromp()->iterateAndNext(*this); puts(", "); + } + } else if (nodep->lhsp()->castVarRef() + && nodep->lhsp()->castVarRef()->varp()->isSc()) { + putbs("VL_ASSIGN_"); // Set a systemC variable + if (nodep->lhsp()->castVarRef()->varp()->isScQuad()) puts("SQ"); + else puts("S"); + emitIQW(nodep); + puts("("); + puts(cvtToStr(nodep->widthMin())+","); + nodep->lhsp()->iterateAndNext(*this); puts(", "); + } else if (nodep->rhsp()->castVarRef() + && nodep->rhsp()->castVarRef()->varp()->isSc()) { + putbs("VL_ASSIGN_"); // Get a systemC variable + emitIQW(nodep); + if (nodep->rhsp()->castVarRef()->varp()->isScQuad()) puts("SQ("); + else puts("S("); + puts(cvtToStr(nodep->widthMin())+","); + nodep->lhsp()->iterateAndNext(*this); puts(", "); + } else if (nodep->isWide() + && nodep->lhsp()->castVarRef() + && !nodep->rhsp()->castVarRef() + && !nodep->rhsp()->castArraySel()) { + // Wide functions assign into the array directly, don't need separate assign statement + m_wideTempRefp = nodep->lhsp()->castVarRef(); + paren = false; + } else if (nodep->isWide()) { + putbs("VL_ASSIGN_W("); + puts(cvtToStr(nodep->widthMin())+","); + nodep->lhsp()->iterateAndNext(*this); puts(", "); + } else { + paren = false; + nodep->lhsp()->iterateAndNext(*this); + puts(" "); + ofp()->blockInc(); decind = true; + if (!nodep->rhsp()->castConst()) ofp()->putBreak(); + puts("= "); + } + nodep->rhsp()->iterateAndNext(*this); + if (paren) puts(")"); + if (decind) ofp()->blockDec(); + if (!m_suppressSemi) puts(";\n"); + } + virtual void visit(AstCCall* nodep, AstNUser*) { + puts(nodep->hiername()); + puts(nodep->funcp()->name()); + puts("("); + for (AstNode* subnodep=nodep->argsp(); subnodep; subnodep = subnodep->nextp()) { + subnodep->accept(*this); + if (subnodep->nextp()) puts(", "); + } + if (nodep->backp()->castNodeMath() || nodep->backp()->castCReturn()) { + // We should have a separate CCall for math and statement usage, but... + puts(")"); + } else { + puts(");\n"); + } + } + virtual void visit(AstNodeCase* nodep, AstNUser*) { + // In V3Case... + nodep->v3fatalSrc("Case statements should have been reduced out\n"); + } + virtual void visit(AstComment* nodep, AstNUser*) { + puts((string)"// "+nodep->name()+" at "+nodep->fileline()->ascii()+"\n"); + nodep->iterateChildren(*this); + } + virtual void visit(AstCoverDecl* nodep, AstNUser*) { + puts("_vlCoverInsert("); // As Declared in emitCoverageDecl + puts("&__Vcoverage["); + puts(cvtToStr(m_coverIds.remap(nodep))); puts("]"); + puts(", \""); puts(nodep->fileline()->filebasename()); puts("\""); + puts(", "); puts(cvtToStr(nodep->fileline()->lineno())); + puts(", "); puts(cvtToStr(nodep->column())); + puts(", \""); puts(nodep->hier()); puts("\""); + puts(", \""); puts(nodep->typeText()); puts("\""); + puts(", \""); puts(nodep->comment()); puts("\""); + puts(");\n"); + } + virtual void visit(AstCoverInc* nodep, AstNUser*) { + puts("if (Verilated::s_coverageRequest)"); + puts(" ++__Vcoverage["); + puts(cvtToStr(m_coverIds.remap(nodep->declp()))); puts("];\n"); + } + virtual void visit(AstCReturn* nodep, AstNUser*) { + puts("return ("); + nodep->lhsp()->iterateAndNext(*this); + puts(");\n"); + } + virtual void visit(AstDisplay* nodep, AstNUser*); // BELOW + virtual void visit(AstFOpen* nodep, AstNUser*) { + nodep->filep()->iterateAndNext(*this); + puts(" = VL_FOPEN_"); + emitIQW(nodep->filenamep()); + emitIQW(nodep->modep()); + if (nodep->modep()->width()>4*8) nodep->modep()->v3error("$fopen mode should be <= 4 characters"); + puts("("); + if (nodep->filenamep()->isWide()) puts(cvtToStr(nodep->filenamep()->widthWords())); + if (nodep->filenamep()->widthWords() > VL_TO_STRING_MAX_WORDS) { + nodep->v3error("String of "<filenamep()->width()<<" bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h\n"); + } + putbs(", "); + nodep->filenamep()->iterateAndNext(*this); + putbs(", "); + nodep->modep()->iterateAndNext(*this); + puts(");\n"); + } + virtual void visit(AstFClose* nodep, AstNUser*) { + puts("if ("); + nodep->filep()->iterateAndNext(*this); + puts(") { fclose (VL_CVT_Q_FP("); + nodep->filep()->iterateAndNext(*this); + puts(")); "); + nodep->filep()->iterateAndNext(*this); // For saftey, so user doesn't later WRITE with it. + puts("=0; }\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 ("); + if (nodep->branchPred() != AstBranchPred::UNKNOWN) { + puts(nodep->branchPred().ascii()); puts("("); + } + nodep->condp()->iterateAndNext(*this); + if (nodep->branchPred() != AstBranchPred::UNKNOWN) puts(")"); + puts(") {\n"); + nodep->ifsp()->iterateAndNext(*this); + if (nodep->elsesp()) { + puts("} else {\n"); + nodep->elsesp()->iterateAndNext(*this); + } + puts("}\n"); + } + virtual void visit(AstStop* nodep, AstNUser*) { + puts("vl_stop(\""); + puts(nodep->fileline()->filename()); + puts("\","); + puts(cvtToStr(nodep->fileline()->lineno())); + puts(",\"\");\n"); + } + virtual void visit(AstFinish* nodep, AstNUser*) { + puts("vl_finish(\""); + puts(nodep->fileline()->filename()); + puts("\","); + puts(cvtToStr(nodep->fileline()->lineno())); + puts(",\"\");\n"); + } + virtual void visit(AstText* nodep, AstNUser*) { + ofp()->putsNoTracking(nodep->text()); + } + virtual void visit(AstCStmt* nodep, AstNUser*) { + nodep->bodysp()->iterateAndNext(*this); + } + virtual void visit(AstCMath* nodep, AstNUser*) { + nodep->bodysp()->iterateAndNext(*this); + } + virtual void visit(AstUCStmt* nodep, AstNUser*) { + puts("// $c statement at "+nodep->fileline()->ascii()+"\n"); + nodep->bodysp()->iterateAndNext(*this); + puts("\n"); + } + virtual void visit(AstUCFunc* nodep, AstNUser*) { + puts("\n"); + puts("// $c function at "+nodep->fileline()->ascii()+"\n"); + nodep->bodysp()->iterateAndNext(*this); + puts("\n"); + } + + // Operators + virtual void visit(AstNodeTermop* nodep, AstNUser*) { + emitOpName(nodep,nodep->emitOperator()); + puts(")"); + } + virtual void visit(AstNodeUniop* nodep, AstNUser*) { + if (emitSimpleOk(nodep)) { + putbs("("); puts(nodep->emitSimpleOperator()); puts(" "); + } else { + emitOpName(nodep,nodep->emitOperator()); + } + nodep->lhsp()->iterateAndNext(*this); puts(")"); + } + virtual void visit(AstNodeBiop* nodep, AstNUser*) { + if (emitSimpleOk(nodep)) { + putbs("("); nodep->lhsp()->iterateAndNext(*this); + puts(" "); putbs(nodep->emitSimpleOperator()); puts(" "); + } else { + emitOpName(nodep,nodep->emitOperator()); + nodep->lhsp()->iterateAndNext(*this); puts(", "); + } + nodep->rhsp()->iterateAndNext(*this); puts(")"); + } + virtual void visit(AstRedXor* nodep, AstNUser* vup) { + if (nodep->lhsp()->isWide()) { + visit(nodep->castNodeUniop(), vup); + } else { + putbs("VL_REDXOR_"); + puts(cvtToStr(nodep->lhsp()->widthPow2())); + puts("("); + nodep->lhsp()->iterateAndNext(*this); + puts(")"); + } + } + virtual void visit(AstMulS* nodep, AstNUser* vup) { + if (nodep->widthWords() > VL_MULS_MAX_WORDS) { + nodep->v3error("Signed multiply of "<width()<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h\n"); + } + visit(nodep->castNodeBiop(), vup); + } + virtual void visit(AstCast* nodep, AstNUser*) { + // Extending a value of the same word width is just a NOP. + if (nodep->size()>VL_WORDSIZE) { + puts("(QData)("); + } else { + puts("(IData)("); + } + nodep->lhsp()->iterateAndNext(*this); + puts(")"); + } + virtual void visit(AstNodeCond* nodep, AstNUser*) { + // Widths match up already, so we'll just use C++'s operator w/o any temps. + if (nodep->expr1p()->isWide()) { + emitOpName(nodep,nodep->emitOperator()); + nodep->condp()->iterateAndNext(*this); puts(", "); + nodep->expr1p()->iterateAndNext(*this); puts(", "); + nodep->expr2p()->iterateAndNext(*this); puts(")"); + } else { + putbs("("); + nodep->condp()->iterateAndNext(*this); putbs(" ? "); + nodep->expr1p()->iterateAndNext(*this); putbs(" : "); + nodep->expr2p()->iterateAndNext(*this); puts(")"); + } + } + virtual void visit(AstSel* nodep, AstNUser*) { + // Note ASSIGN checks for this on a LHS + if (nodep->widthp()->isOne()) { + emitOpName(nodep,"VL_BITSEL"); + nodep->fromp()->iterateAndNext(*this); puts(", "); + nodep->lsbp()->iterateAndNext(*this); puts(")"); + } else { + emitOpName(nodep,"VL_SEL"); + nodep->fromp()->iterateAndNext(*this); puts(", "); + nodep->lsbp()->iterateAndNext(*this); puts(", "); + nodep->widthp()->iterateAndNext(*this); puts(")"); + } + } + virtual void visit(AstReplicate* nodep, AstNUser*) { + if (nodep->lhsp()->widthMin() == 1 && !nodep->isWide()) { + if (((int)nodep->rhsp()->castConst()->asInt() + * nodep->lhsp()->widthMin()) != nodep->widthMin()) + nodep->v3fatalSrc("Replicate non-constant or width miscomputed"); + puts("VL_REPLICATE_"); + emitIQW(nodep); + puts("OI("); + puts(cvtToStr(nodep->widthMin())); + if (nodep->lhsp()) { puts(","+cvtToStr(nodep->lhsp()->widthMin())); } + if (nodep->rhsp()) { puts(","+cvtToStr(nodep->rhsp()->widthMin())); } + puts(","); + } else { + emitOpName(nodep,nodep->emitOperator()); + } + nodep->lhsp()->iterateAndNext(*this); puts(", "); + nodep->rhsp()->iterateAndNext(*this); puts(")"); + } + virtual void visit(AstArraySel* nodep, AstNUser*) { + nodep->fromp()->iterateAndNext(*this); putbs("["); + nodep->bitp()->iterateAndNext(*this); puts("]"); + } + virtual void visit(AstWordSel* nodep, AstNUser*) { + nodep->fromp()->iterateAndNext(*this); puts("["); // Not putbs, as usually it's a small constant next + nodep->bitp()->iterateAndNext(*this); puts("]"); + } + // Terminals + virtual void visit(AstVarRef* nodep, AstNUser*) { + puts(nodep->hiername()); + puts(nodep->varp()->name()); + } + void emitConstant(AstConst* nodep, AstVarRef* assigntop, const string& assignString) { + // Put out constant set to the specified variable, or given variable in a string + if (nodep->num().isFourState()) { + nodep->v3error("Unsupported: 4-state numbers in this context"); + } else if (nodep->isWide()) { + putbs("VL_CONST_W_"); + puts(cvtToStr(VL_WORDS_I(nodep->num().minWidth()))); + puts("X("); + puts(cvtToStr(nodep->widthMin())); + puts(","); + if (!assigntop) { + puts(assignString); + } else if (assigntop->castVarRef()) { + puts(assigntop->hiername()); + puts(assigntop->varp()->name()); + } else { + assigntop->iterateAndNext(*this); + } + for (int word=VL_WORDS_I(nodep->num().minWidth())-1; word>0; word--) { + ofp()->printf(",0x%08x", nodep->num().dataWord(word)); + } + ofp()->printf(",0x%08x)", nodep->num().dataWord(0)); + } else if (nodep->isQuad()) { + vluint64_t num = nodep->asQuad(); + if (num<10) ofp()->printf("VL_ULL(%lld)", (long long)num); + else ofp()->printf("VL_ULL(0x%llx)", (long long)num); + } else { + uint32_t num = nodep->asInt(); + if (num<10) puts(cvtToStr(num)); + else ofp()->printf("0x%x", num); + //Unneeded-Causes %lx format warnings: + // if (!nodep->num().isSigned() && (num & (1UL<<31))) puts("U"); + } + } + void emitSetVarConstant(const string& assignString, AstConst* constp) { + if (!constp->isWide()) { + puts(assignString); + puts(" = "); + } + emitConstant(constp, NULL, assignString); + puts(";\n"); + } + virtual void visit(AstConst* nodep, AstNUser*) { + if (nodep->isWide()) { + if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Constant w/ no temp"); + emitConstant(nodep, m_wideTempRefp, ""); + m_wideTempRefp = NULL; // We used it, barf if set it a second time + } else { + emitConstant(nodep, NULL, ""); + } + } + + // Just iterate + virtual void visit(AstTopScope* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + virtual void visit(AstScope* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + // NOPs + virtual void visit(AstPragma*, AstNUser*) {} + virtual void visit(AstCell*, AstNUser*) {} // Handled outside the Visit class + virtual void visit(AstVar*, AstNUser*) {} // Handled outside the Visit class + virtual void visit(AstNodeText*, AstNUser*) {} // Handled outside the Visit class + virtual void visit(AstTraceDecl*, AstNUser*) {} // Handled outside the Visit class + virtual void visit(AstTraceInc*, 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: "<typeName()); + } + +public: + EmitCStmts() { + m_suppressSemi = false; + m_wideTempRefp = NULL; + } + virtual ~EmitCStmts() {} +}; + +//###################################################################### +// Internal EmitC implementation + +class EmitCImp : EmitCStmts { + // MEMBERS + AstModule* m_modp; + vector m_blkChangeDetVec; // All encountered changes in block + bool m_slow; // Creating __Slow file + bool m_fast; // Creating non __Slow file (or both) + int m_splitSize; // # of cfunc nodes placed into output file + int m_splitFilenum; // File number being created, 0 = primary + + //--------------------------------------- + // METHODS + + void doubleOrDetect(AstChangeDet* changep, bool& gotOne) { + static int addDoubleOr = 10; // Determined experimentally as best + if (!changep->rhsp()) { + if (!gotOne) gotOne = true; + else puts(" | "); + changep->lhsp()->iterateAndNext(*this); + } + else { + AstVarRef* lhsp = changep->lhsp()->castVarRef(); + AstVarRef* rhsp = changep->rhsp()->castVarRef(); + if (!lhsp) changep->v3fatalSrc("Not ref?"); + if (!rhsp) changep->v3fatalSrc("Not ref?"); + for (int word=0; wordlhsp()->widthWords(); word++) { + if (!gotOne) { + gotOne = true; + addDoubleOr = 10; // Determined experimentally as best + puts("("); + } else if (--addDoubleOr == 0) { + puts("|| ("); + addDoubleOr = 10; + } else { + puts(" | ("); + } + changep->lhsp()->iterateAndNext(*this); + if (changep->isWide()) puts("["+cvtToStr(word)+"]"); + puts(" ^ "); + changep->rhsp()->iterateAndNext(*this); + if (changep->isWide()) puts("["+cvtToStr(word)+"]"); + puts(")"); + } + } + } + + V3OutCFile* newOutCFile(AstModule* modp, bool slow, bool source, int filenum=0) { + string filenameNoExt = v3Global.opt.makeDir()+"/"+ modClassName(modp); + if (filenum) filenameNoExt += "__"+cvtToStr(filenum); + filenameNoExt += (slow ? "__Slow":""); + V3OutCFile* ofp = NULL; + if (optSystemPerl()) { + string filename = filenameNoExt+".sp"; + newCFile(filename, slow, source); + ofp = new V3OutSpFile (filename); + } + else if (optSystemC()) { + string filename = filenameNoExt+(source?".cpp":".h"); + newCFile(filename, slow, source); + ofp = new V3OutScFile (filename); + } + else { + string filename = filenameNoExt+(source?".cpp":".h"); + newCFile(filename, slow, source); + ofp = new V3OutCFile (filename); + } + ofp->putsHeader(); + return ofp; + } + + //--------------------------------------- + // VISITORS + virtual void visit(AstCFunc* nodep, AstNUser*) { + if (nodep->funcType() == AstCFuncType::TRACE_INIT + || nodep->funcType() == AstCFuncType::TRACE_FULL + || nodep->funcType() == AstCFuncType::TRACE_CHANGE) { + return; // Handled specially + } + if (!(nodep->slow() ? m_slow : m_fast)) return; + + m_blkChangeDetVec.clear(); + + m_splitSize += EmitCBaseCounterVisitor(nodep).count(); + + puts("\n"); + puts(nodep->rtnTypeVoid()); puts(" "); + puts(modClassName(m_modp)+"::"+nodep->name() + +"("+cFuncArgs(nodep)+") {\n"); + + puts("VL_DEBUG_IF(cout<<\""); + for (int i=0;ilevel();i++) { puts(" "); } + puts(modClassName(m_modp)+"::"+nodep->name() + +" \"" + +(optSystemC()?"<initsp()) puts("// Variables\n"); + ofp()->putAlign(4); + for (AstNode* subnodep=nodep->argsp(); subnodep; subnodep = subnodep->nextp()) { + if (AstVar* varp=subnodep->castVar()) { + if (varp->isFuncReturn()) emitVarDecl(varp, ""); + } + } + emitVarList(nodep->initsp(), EVL_ALL, ""); + ofp()->putAlign(4); + emitVarList(nodep->stmtsp(), EVL_ALL, ""); + ofp()->putAlign(4); + + nodep->initsp()->iterateAndNext(*this); + + if (nodep->stmtsp()) puts("// Body\n"); + nodep->stmtsp()->iterateAndNext(*this); +#ifndef NEW_ORDERING + if (!m_blkChangeDetVec.empty()) emitChangeDet(); +#endif + + if (nodep->finalsp()) puts("// Final\n"); + nodep->finalsp()->iterateAndNext(*this); + // + + if (!m_blkChangeDetVec.empty()) puts("return __req;\n"); + +// puts("__Vm_activity = true;\n"); + puts("}\n"); + } + + void emitChangeDet() { + puts("// Change detection\n"); + puts("IData __req = false; // Logically a bool\n"); // But not because it results in faster code + bool gotOne = false; + for (vector::iterator it = m_blkChangeDetVec.begin(); + it != m_blkChangeDetVec.end(); ++it) { + AstChangeDet* changep = *it; + if (changep->lhsp()) { + if (!gotOne) { // Not a clocked block + puts("__req |= ("); + } + else puts("\n"); + doubleOrDetect(changep, gotOne); + } + } + if (gotOne) { + puts(");\n"); + //puts("VL_DEBUG_IF( if (__req) cout<<\"\tCLOCKREQ );"); + } + } + + virtual void visit(AstChangeDet* nodep, AstNUser*) { + m_blkChangeDetVec.push_back(nodep); + } + + //--------------------------------------- + // ACCESSORS + + // METHODS + // Low level + void emitVarResets(AstModule* modp); + void emitCellCtors(AstModule* modp); + void emitSensitives(); + // Medium level + void emitCoverageCtor(AstModule* modp); + void emitCoverageDecl(AstModule* modp); + void emitTextSection(AstType type); + void emitIntFuncDecls(AstModule* modp); + // High level + void emitImp(AstModule* modp); + void emitImpBottom(AstModule* modp); + void emitStaticDecl(AstModule* modp); + void emitWrapEval(AstModule* modp); + void emitInt(AstModule* modp); + void writeMakefile(string filename); + +public: + EmitCImp() { + m_modp = NULL; + m_splitSize = 0; + m_splitFilenum = 0; + } + virtual ~EmitCImp() {} + void main(AstModule* modp, bool slow, bool fast); + void mainDoFunc(AstCFunc* nodep) { + nodep->accept(*this); + } + int splitSize() { return m_splitSize; } +}; + +//###################################################################### +// Internal EmitCStmts + +void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) { + if (nodep->isIO()) { + if (nodep->isSc()) { + m_ctorVarsVec.push_back(nodep); + ofp()->putAlign(4); // sc stuff is a structure, so bigger alignment + if (nodep->attrScClocked() && nodep->isInput()) { + puts("sc_in_clk\t"); + } else { + if (nodep->isTristate()) puts("sc_inout<"); + else if (nodep->isInput()) puts("sc_in<"); + else if (nodep->isOutput()) puts("sc_out<"); + else nodep->v3fatalSrc("Unknown type"); + + puts(nodep->cType()); + puts(">\t"); + } + puts(nodep->name()); + puts(";\n"); + } else { // C++ signals + ofp()->putAlign(nodep->widthAlignBytes()); + if (nodep->isTristate()) puts("VL_INOUT"); + else if (nodep->isInput()) puts("VL_IN"); + else if (nodep->isOutput()) puts("VL_OUT"); + else nodep->v3fatalSrc("Unknown type"); + + if (nodep->isQuad()) puts("64"); + else if (nodep->widthMin() <= 8) puts("8"); + else if (nodep->widthMin() <= 16) puts("16"); + + if (!nodep->isWide()) + puts("("+nodep->name() + +","+cvtToStr(nodep->msb()) + +","+cvtToStr(nodep->lsb())); + else puts("W("+nodep->name() + +","+cvtToStr(nodep->msb()) + +","+cvtToStr(nodep->lsb()) + +","+cvtToStr(nodep->widthWords())); + puts(");\n"); + } + } else { + // Arrays need a small alignment, but may need different padding after. + // For example three VL_SIG8's needs alignment 1 but size 3. + ofp()->putAlign(nodep->widthAlignBytes(), nodep->arrayElements()*nodep->widthAlignBytes()); + // if (nodep->isStatic()) puts("static "); // Need implementation declaration too - prefixIfImp is start in this direction + if (nodep->widthMin() <= 8) { + puts("VL_SIG8("); + } else if (nodep->widthMin() <= 16) { + puts("VL_SIG16("); + } else if (nodep->isQuad()) { + puts("VL_SIG64("); + } else if (!nodep->isWide()) { + puts("VL_SIG("); + } else { + puts("VL_SIGW("); + } + if (prefixIfImp!="") { puts(prefixIfImp); puts("::"); } + puts(nodep->name()); + for (AstRange* arrayp=nodep->arraysp(); arrayp; arrayp = arrayp->nextp()->castRange()) { + puts("["+cvtToStr(arrayp->elementsConst())+"]"); + } + puts(","+cvtToStr(nodep->msb())+","+cvtToStr(nodep->lsb())); + if (nodep->isWide()) puts(","+cvtToStr(nodep->widthWords())); + puts(");\n"); + } +} + +void EmitCStmts::emitVarCtors() { + ofp()->indentInc(); + bool first = true; + for (vector::iterator it = m_ctorVarsVec.begin(); it != m_ctorVarsVec.end(); ++it) { + if (first) { + first=false; + puts("\n"); + puts("#if (SYSTEMC_VERSION>20011000)\n"); // SystemC 2.0.1 and newer + puts(" : "); + } + else puts(", "); + if (ofp()->exceededWidth()) puts("\n "); + puts((*it)->name()); puts("(\""); + puts((*it)->name()); puts("\")"); + } + if (!first) puts ("\n#endif\n"); + ofp()->indentDec(); +} + +bool EmitCStmts::emitSimpleOk(AstNodeMath* nodep) { + // Can we put out a simple (A + B) instead of VL_ADD_III(A,B)? + if (nodep->emitSimpleOperator() == "") return false; + if (nodep->isWide()) return false; + if (nodep->op1p()) { if (nodep->op1p()->isWide()) return false; } + if (nodep->op2p()) { if (nodep->op2p()->isWide()) return false; } + if (nodep->op3p()) { if (nodep->op3p()->isWide()) return false; } + return true; +} + +void EmitCStmts::emitOpName(AstNode* nodep, const string& opname) { + putbs(opname+"_"); + if (nodep->emitWordForm()) { + emitIQW(nodep->op1p()); + puts("("); + if (nodep->op1p()->isWide()) { + puts(cvtToStr(nodep->op1p()->widthWords())); + puts(", "); + } + } else { + emitIQW(nodep); + if (nodep->op1p()) { emitIQW(nodep->op1p()); } + if (nodep->op2p()) { emitIQW(nodep->op2p()); } + if (nodep->op3p()) { emitIQW(nodep->op3p()); } + puts("("); + puts(cvtToStr(nodep->widthMin())); + if (nodep->op1p()) { puts(","+cvtToStr(nodep->op1p()->widthMin())); } + if (nodep->op2p()) { puts(","+cvtToStr(nodep->op2p()->widthMin())); } + if (nodep->op3p()) { puts(","+cvtToStr(nodep->op3p()->widthMin())); } + if (nodep->op1p() || nodep->isWide()) puts(", "); + } + if (nodep->isWide()) { + if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Op w/ no temp, perhaps missing op in V3EmitC?"); + puts(m_wideTempRefp->hiername()); + puts(m_wideTempRefp->varp()->name()); + m_wideTempRefp = NULL; + if (nodep->op1p()) puts(", "); + } +} + +//---------------------------------------------------------------------- +// Mid level - VISITS + +// We only do one display at once, so can just use static state + +struct EmitDispState { + bool m_wide; // Put out a wide func that needs string buffer + string m_format; // "%s" and text from user + vector m_argsp; // Each argument to be printed + vector m_argsFunc; // Function before each argument to be printed + EmitDispState() { clear(); } + void clear() { + m_wide = false; + m_format = ""; + m_argsp.clear(); + m_argsFunc.clear(); + } + void pushFormat(const string& fmt) { m_format += fmt; } + void pushFormat(char fmt) { m_format += fmt; } + void pushArg(AstNode* nodep, const string& func) { + m_argsp.push_back(nodep); m_argsFunc.push_back(func); + } +} emitDispState; + +void EmitCStmts::displayEmit(AstDisplay* nodep) { + if (emitDispState.m_format != "") { + // Format + if (nodep->filep()) { + puts("if ("); + nodep->filep()->iterate(*this); // Check if closed, to avoid core dump + puts(") fprintf(VL_CVT_Q_FP("); + nodep->filep()->iterate(*this); + puts("),\""); + } else { + puts("VL_PRINTF(\""); + } + ofp()->putsNoTracking(emitDispState.m_format); + puts("\""); + // Arguments + for (unsigned i=0; i < emitDispState.m_argsp.size(); i++) { + puts(","); + AstNode* argp = emitDispState.m_argsp[i]; + string func = emitDispState.m_argsFunc[i]; + ofp()->indentInc(); + ofp()->putbs(""); + if (func!="") puts(func); + if (argp) argp->iterate(*this); + if (func!="") puts(")"); + ofp()->indentDec(); + } + // End + puts(");\n"); + // Prep for next + emitDispState.clear(); + } +} + +string EmitCStmts::displayFormat(AstNode* widthNodep, string in, + char fmtLetter, bool padZero, bool reallyString) { + if (fmtLetter=='s') padZero = false; + if (widthNodep && widthNodep->isWide() + && (fmtLetter=='d'||fmtLetter=='u')) { + widthNodep->v3error("Unsupported: $display of dec format of > 64 bit results (use hex format instead)"); + } + if (widthNodep && widthNodep->widthMin()>8 && fmtLetter=='c') { + widthNodep->v3error("$display of char format of > 8 bit result"); + } + string fmt; + if (in == "") { + // Size naturally + if (widthNodep == NULL) fmt=""; // Out of args, will get error + if (fmtLetter=='u' || fmtLetter=='d') { // Decimal. Spec says leading spaces, not zeros + double mantissabits = widthNodep->widthMin() - ((fmtLetter=='d')?1:0); + double maxval = pow(2.0, mantissabits); + double dchars = log10(maxval)+1.0; + if (fmtLetter=='d') dchars++; // space for sign + int nchars = int(dchars); + fmt=cvtToStr(nchars); + } else if (fmtLetter!='s' && fmtLetter!='c') { // Strings/chars don't get padding + int bitsPerChar = (fmtLetter=='b'?1 : fmtLetter=='o'?3 : 4); + int nchars = (widthNodep->widthMin() + bitsPerChar-1)/bitsPerChar; + if (padZero) fmt=(string)("0")+cvtToStr(nchars); + else fmt=cvtToStr(nchars); + } + } else if (in == "0") { + fmt=""; // No width + } else { + fmt=in; + } + if (widthNodep->isQuad() && fmtLetter!='c' && fmtLetter!='s' && !reallyString) fmt+="ll"; + return fmt; +} + +void EmitCStmts::displayArg(AstDisplay* dispp, AstNode** elistp, string fmt, char fmtLetter) { + // Print display argument, edits elistp + if (!*elistp) { + dispp->v3error("Missing arguments for $display format"); + return; + } + if ((*elistp)->widthMin() > VL_VALUE_STRING_MAX_WIDTH) { + dispp->v3error("Exceeded limit of 1024 bits for any display arguments"); + } + + if ((*elistp)->isWide() // Have to use our own function for wide, + || fmtLetter=='s' // ... Verilog strings + || fmtLetter=='b' // ... binary, no printf %b in C + || fmtLetter=='d') { // ... Signed decimal + // We use a single static string, so must only have one call per VL_PRINT + if (emitDispState.m_wide) { displayEmit(dispp); } + emitDispState.m_wide = true; + string nfmt = displayFormat(*elistp, fmt, fmtLetter, false, true); + string pfmt = "%"+nfmt+"s"; + string func = "VL_VALUE_FORMATTED_"; + func += ((*elistp)->isWide()) ? "W(" : ((*elistp)->isQuad()) ? "Q(" : "I("; + func += (cvtToStr((*elistp)->widthMin()) + +",'"+fmtLetter+"'" + +","+(fmt=="0"?"true":"false")+","); + emitDispState.pushFormat(pfmt); + emitDispState.pushArg(*elistp,func); + } else { + string nfmt = displayFormat(*elistp, fmt, fmtLetter, true, false); + string pfmt = "%"+nfmt+fmtLetter; + emitDispState.pushFormat(pfmt); + emitDispState.pushArg(*elistp,""); + } + // Next parameter + *elistp = (*elistp)->nextp(); +} + +void EmitCStmts::visit(AstDisplay* nodep, AstNUser*) { + string vformat = nodep->text(); + bool addNewline = (nodep->newline() == '\n'); + AstNode* elistp = nodep->exprsp(); + + // Convert Verilog display to C printf formats + // "%0t" becomes "%d" + emitDispState.clear(); + string fmt = ""; + string::iterator pos = vformat.begin(); + if (*pos == '"') pos++; + bool inPct = false; + for (; pos != vformat.end(); ++pos) { + if (pos[0]=='"' && (pos+1)==vformat.end()) break; + if (inPct && pos[0]=='%') { + emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the % + inPct = false; + } else if (pos[0]=='%') { + inPct = true; + fmt = ""; + } else if (!inPct) { // Normal text + //char text[2]; text[0]=*pos; text[1]='\0'; + emitDispState.pushFormat(*pos); + } else { // Format character + if (isdigit(pos[0])) { + // Digits, like %5d, etc. + fmt += pos[0]; + } else { + inPct = false; + switch (tolower(pos[0])) { + // Special codes + case '~': displayArg(nodep,&elistp,fmt,'d'); break; // Signed decimal + // Spec: h d o b c l + case 'b': displayArg(nodep,&elistp,fmt,'b'); break; + case 'c': displayArg(nodep,&elistp,fmt,'c'); break; + case 'd': displayArg(nodep,&elistp,fmt,'u'); break; // Unsigned decimal + case 'o': displayArg(nodep,&elistp,fmt,'o'); break; + case 'h': + case 'x': displayArg(nodep,&elistp,fmt,'x'); break; + case 's': displayArg(nodep,&elistp,fmt,'s'); break; + case 't': displayArg(nodep,&elistp,fmt,'u'); break; + case 'm': { + emitDispState.pushFormat("%s"); + emitDispState.pushArg(NULL, "name("); + for (AstText* textp=nodep->scopeTextp(); textp; textp=textp->nextp()->castText()) { + emitDispState.pushFormat(textp->text()); + } + break; + } + case 'u': + case 'z': + case 'l': + case 'v': + nodep->v3error("Unsupported: $display format code: %"<v3error("Unknown $display format code: %"<v3error("Extra arguments for $display format\n"); + } + if (addNewline) emitDispState.pushFormat("\\n"); + displayEmit(nodep); +} + +//###################################################################### +// Internal EmitC + +void EmitCImp::emitVarResets(AstModule* modp) { + puts("// Reset internal values\n"); + puts("__Vm_inhibitSim = false;\n"); + if (modp->isTop()) { + puts("__Vm_activity = false;\n"); + puts("__Vm_didInit = false;\n"); + puts("\n"); + } + + puts("// Reset structure values\n"); + for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstVar* varp = nodep->castVar()) { + if (varp->isIO() && modp->isTop() && optSystemC()) { + // System C top I/O doesn't need loading, as the lower level subinst code does it. + } + else if (varp->isParam()) { + if (!varp->hasSimpleInit()) nodep->v3fatalSrc("No init for a param?"); + //puts("// parameter "+varp->name()+" = "+varp->initp()->name()+"\n"); + } + else if (AstInitArray* initarp = varp->initp()->castInitArray()) { + AstConst* constsp = initarp->initsp()->castConst(); + if (!varp->arraysp()) varp->v3fatalSrc("InitArray under non-arrayed var"); + for (int i=0; iarraysp()->elementsConst(); i++) { + if (!constsp) initarp->v3fatalSrc("Not enough values in array initalizement"); + emitSetVarConstant(varp->name()+"["+cvtToStr(i)+"]", constsp); + constsp = constsp->nextp()->castConst(); + } + } + else { + int vects = 0; + for (AstRange* arrayp=varp->arraysp(); arrayp; arrayp = arrayp->nextp()->castRange()) { + int vecnum = vects++; + if (arrayp->msbConst() < arrayp->lsbConst()) varp->v3fatalSrc("Should have swapped msb & lsb earlier."); + string ivar = string("__Vi")+cvtToStr(vecnum); + // MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block + puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(0)+";"); + puts(" for (; "+ivar+"<"+cvtToStr(arrayp->elementsConst())); + puts("; ++"+ivar+") {\n"); + } + bool zeroit = (varp->attrFileDescr() // Zero it out, so we don't core dump if never call $fopen + || (varp->name().c_str()[0]=='_' && v3Global.opt.underlineZero())); + if (varp->isWide()) { + // DOCUMENT: We randomize everything. If the user wants a _var to be zero, + // there should be a initial statement. (Different from verilator2.) + if (zeroit) puts("VL_ZERO_RESET_W("); + else puts("VL_RAND_RESET_W("); + puts(cvtToStr(varp->widthMin())); + puts(","); + puts(varp->name()); + for (int v=0; vname()); + for (int v=0; vwidthMin())); + puts(");\n"); + } + } + for (int v=0; vstmtsp(); nodep; nodep = nodep->nextp()) { + if (nodep->castCoverDecl()) { + if (first) { + first = false; + puts("// Coverage Declarations\n"); + } + nodep->accept(*this); + } + } +} + +void EmitCImp::emitCoverageDecl(AstModule* modp) { + m_coverIds.clear(); + for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCoverDecl* declp = nodep->castCoverDecl()) { + m_coverIds.remap(declp); + } + } + if (m_coverIds.size()) { + ofp()->putsPrivate(true); + puts("// Coverage\n"); + puts("SpZeroed\t__Vcoverage["); puts(cvtToStr(m_coverIds.size())); puts("];\n"); + ofp()->putAlign(sizeof(uint32_t)*m_coverIds.size()); + // Rather then putting out SP_COVER_INSERT calls directly, we do it via this function + // This gets around gcc slowness constructing all of the template arguments + puts("void _vlCoverInsert(SpZeroed* countp, const char* filename, int lineno, int column,\n"); + puts( "const char* hier, const char* type, const char* comment) {\n"); + puts( "SP_COVER_INSERT(countp,"); + puts( " \"filename\",filename,"); + puts( " \"lineno\",lineno,"); + puts( " \"column\",column,\n"); + puts( "\"hier\",string(name())+hier,"); + puts( " \"type\",type,"); + puts( " \"comment\",comment);\n"); + puts("}\n"); + } +} + +void EmitCImp::emitStaticDecl(AstModule* modp) { + // Need implementation here. Be careful of alignment code; needs to be uniquified + // with module name to avoid multiple symbols. + //emitVarList(modp->stmtsp(), EVL_ALL, modp->name()); +} + +void EmitCImp::emitTextSection(AstType type) { + int last_line = -999; + for (AstNode* nodep = m_modp->stmtsp(); nodep != NULL; nodep = nodep->nextp()) { + if (AstNodeText* textp = nodep->castNodeText()) { + if (nodep->type() == type) { + if (last_line != nodep->fileline()->lineno()) { + if (last_line < 0) { + puts("\n//*** Below code from `systemc in Verilog file\n"); + } + ofp()->putsNoTracking("//#line "+cvtToStr(nodep->fileline()->lineno()) + +" \""+nodep->fileline()->filename()+"\"\n"); + last_line = nodep->fileline()->lineno(); + } + ofp()->putsNoTracking(textp->text()); + last_line++; + } + } + } + if (last_line > 0) { + puts("//*** Above code from `systemc in Verilog file\n\n"); + } +} + +void EmitCImp::emitCellCtors(AstModule* modp) { + if (modp->isTop()) { + puts("__VlSymsp = new "+symClassName()+"(this);\n"); + } + for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCell* cellp=nodep->castCell()) { + puts("VL_CELL ("+cellp->name()+", "+modClassName(cellp->modp())+");\n"); + } + } +} + +void EmitCImp::emitSensitives() { + // Create sensitivity list for when to evaluate the model. + // If C++ code, the user must call this routine themself. + if (m_modp->isTop() && optSystemC()) { + puts("// Sensitivities on all clocks and combo inputs\n"); + puts("SC_METHOD(eval);\n"); + for (AstNode* nodep=m_modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstVar* varp = nodep->castVar()) { + if (varp->isInput() && (varp->isScSensitive() || varp->isUsedClock())) { + puts("sensitive("+varp->name()+");\n"); + } + } + } + puts("\n"); + } +} + +void EmitCImp::emitWrapEval(AstModule* modp) { + puts("\nvoid "+modClassName(modp)+"::eval() {\n"); + puts("// Setup global symbol table\n"); + puts(symClassName()); puts("::init(this);\n"); + puts("// Initialize\n"); + puts("if (!__Vm_didInit) eval_initial_loop();\n"); + puts("// Evaluate till stable\n"); + puts("VL_DEBUG_IF(cout<<\"\\n----TOP Evaluate "+modClassName(modp)+"::eval\"< 100) vl_fatal(__FILE__,__LINE__,__FILE__,\"Verilated model didn't converge\");\n"); + puts("}\n"); +#endif + puts("}\n"); + + // + puts("\nvoid "+modClassName(modp)+"::eval_initial_loop() {\n"); + puts("__Vm_didInit = true;\n"); + puts("_eval_initial();\n"); +#ifndef NEW_ORDERING + puts( "__Vm_activity = true;\n"); + puts( "int __VclockLoop = 0;\n"); + puts( "IData __Vchange;\n"); + puts( "for (__Vchange=1; __Vchange; ) {\n"); +#endif + puts( "_eval_settle();\n"); + puts( "_eval();\n"); +#ifndef NEW_ORDERING + puts( "__Vchange = _change_request();\n"); + puts( "if (++__VclockLoop > 100) vl_fatal(__FILE__,__LINE__,__FILE__,\"Verilated model didn't DC converge\");\n"); + puts( "}\n"); +#endif + puts("}\n"); +} + +//---------------------------------------------------------------------- +// Top interface/ implementation + +void EmitCStmts::emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp) { + // Put out a list of signal declarations + // in order of 0:clocks, 1:uint8, 2:uint16, 4:uint32, 5:uint64, 6:wide, 7:arrays + // This aids cache packing and locality + for (int size=0; size<8; size++) { + if (size==3) continue; + for (AstNode* nodep=firstp; nodep; nodep = nodep->nextp()) { + if (AstVar* varp = nodep->castVar()) { + bool doit = true; + switch (which) { + case EVL_ALL: doit = true; break; + case EVL_IO: doit = varp->isIO(); break; + case EVL_SIG: doit = (varp->isSignal() && !varp->isIO()); break; + case EVL_TEMP: doit = varp->isTemp(); break; + default: v3fatalSrc("Bad Case"); + } + if (doit) { + int sigbytes = varp->widthAlignBytes(); + if (varp->isUsedClock() && varp->widthMin()==1) sigbytes = 0; + else if (varp->arraysp()) sigbytes=7; + else if (varp->isScWide()) sigbytes=6; + else if (sigbytes==8) sigbytes=5; + else if (sigbytes==4) sigbytes=4; + else if (sigbytes==2) sigbytes=2; + else if (sigbytes==1) sigbytes=1; + if (size==sigbytes) { + emitVarDecl(varp, prefixIfImp); + } + } + } + } + } + ofp()->putAlign(4, 0, prefixIfImp.c_str()); +} + +struct CmpName { + inline bool operator () (const AstNode* lhsp, const AstNode* rhsp) const { + return lhsp->name() < rhsp->name(); + } +}; + +void EmitCImp::emitIntFuncDecls(AstModule* modp) { + vector funcsp; + + for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCFunc* funcp = nodep->castCFunc()) { + if (!funcp->skipDecl()) { + funcsp.push_back(funcp); + } + } + } + + sort(funcsp.begin(), funcsp.end(), CmpName()); + + for (vector::iterator it = funcsp.begin(); it != funcsp.end(); ++it) { + AstCFunc* funcp = *it; + ofp()->putsPrivate(funcp->declPrivate()); + puts(funcp->rtnTypeVoid()); puts("\t"); + puts(funcp->name()); puts("("+cFuncArgs(funcp)+");\n"); + } +} + +void EmitCImp::emitInt(AstModule* modp) { + // Always have this first; gcc has short circuiting if #ifdef is first in a file + if (!optSystemPerl()) { // else done for us automatically + puts("#ifndef _"+modClassName(modp)+"_H_\n"); + puts("#define _"+modClassName(modp)+"_H_\n"); + puts("\n"); + } + + ofp()->putsIntTopInclude(); + puts("#include \"verilated.h\"\n"); + if (v3Global.opt.coverage()) { + puts("#include \"SpCoverage.h\"\n"); + } + + // Declare foreign instances up front to make C++ happy + puts("class "+symClassName()+";\n"); + for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCell* cellp=nodep->castCell()) { + puts("class "+modClassName(cellp->modp())+";\n"); + } + } + + puts("\n//----------\n\n"); + emitTextSection(AstType::SCHDR); + + if (optSystemC() && modp->isTop()) { + puts("SC_MODULE("+modClassName(modp)+") {\n"); + } else { + puts("VL_MODULE("+modClassName(modp)+") {\n"); + } + if (optSystemPerl()) puts("/*AUTOATTR(verilated)*/\n\n"); + ofp()->resetPrivate(); + ofp()->putsPrivate(false); // public: + + // Instantiated modules + if (optSystemPerl()) { + puts("/*AUTOSUBCELLS*/\n\n"); + } else { + puts("// CELLS\n"); + for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCell* cellp=nodep->castCell()) { + ofp()->putsCellDecl(modClassName(cellp->modp()), cellp->name()); + } + } + } + + puts("\n// PORTS\n"); + emitVarList(modp->stmtsp(), EVL_IO, ""); + + puts("\n// LOCAL SIGNALS\n"); + emitVarList(modp->stmtsp(), EVL_SIG, ""); + + puts("\n// LOCAL VARIABLES\n"); + emitVarList(modp->stmtsp(), EVL_TEMP, ""); + + puts("\n// INTERNAL VARIABLES\n"); + ofp()->putAlign(sizeof(bool)); + ofp()->putsPrivate(!modp->isTop()); // private: unless top + ofp()->putAlign(8); + puts(symClassName()+"*\t__VlSymsp;\t\t// Symbol table\n"); + ofp()->putsPrivate(false); // public: + puts("bool\t__Vm_inhibitSim;\t///< Manually set true to disable evaluation of module\n"); + if (modp->isTop()) { + ofp()->putAlign(sizeof(bool)); + puts("bool\t__Vm_activity;\t\t///< Used by trace routines to determine change occurred\n"); + ofp()->putAlign(sizeof(bool)); + puts("bool\t__Vm_didInit;\n"); + } + ofp()->putAlign(8); + emitCoverageDecl(modp); // may flip public/private + ofp()->putAlign(8); + + puts("\n// PARAMETERS\n"); + ofp()->putsPrivate(false); // public: + for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstVar* varp = nodep->castVar()) { + if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { + if (!varp->initp()) nodep->v3fatalSrc("No init for a param?"); + // These should be static const values, however microsloth VC++ doesn't + // support them. They also cause problems with GDB under GCC2.95. + if (varp->isScWide()) { // Unsupported for output + puts("// enum WData "+varp->name()+" //wide"); + } else if (!varp->initp()->castConst()) { // Unsupported for output + puts("// enum IData "+varp->name()+" //not simple value"); + } else { + puts("enum "); + puts(varp->isQuad()?"_QData":"_IData"); + puts(""+varp->name()+" { "+varp->name()+" = "); + varp->initp()->iterateAndNext(*this); + puts("};"); + } + puts("\n"); + } + } + } + + puts("\n// METHODS\n"); + ofp()->resetPrivate(); + ofp()->putsPrivate(false); // public: + if (optSystemC() && modp->isTop()) { + puts("SC_CTOR("+modClassName(modp)+");\n"); + } else if (optSystemC()) { + puts("VL_CTOR("+modClassName(modp)+");\n"); + } else { + puts(modClassName(modp)+"(const char* name=\"TOP\");\n"); + if (v3Global.opt.trace()) { + puts("void\ttrace (SpTraceVcdCFile* tfp, int levels, int options=0);\n"); + } + } + if (!modp->isTop()) { + puts("void\t__Vconfigure("+symClassName()+"*\tsymsp) { __VlSymsp = symsp; } ///< Internal symbol table construction\n"); + } + if (optSystemPerl()) puts("/*AUTOMETHODS*/\n"); + + if (modp->isTop()) { + puts("inline bool\tgetClearActivity() { bool r=__Vm_activity; __Vm_activity=false; return r;}\n"); + } + emitTextSection(AstType::SCINT); + + puts("\n// Sensitivity blocks\n"); + if (modp->isTop()) { + puts("void\tfinal();\t///< Function to call when simulation completed\n"); + if (optSystemC()) ofp()->putsPrivate(true); ///< eval() is invoked by our sensitive() calls. + puts("void\teval();\t///< Main function to call from calling app when inputs change\n"); + ofp()->putsPrivate(true); // private: + puts("void\teval_initial_loop();\n"); +#ifndef NEW_ORDERING + puts("IData\tchange_request();\n"); +#endif + } + + emitIntFuncDecls(modp); + + if (!optSystemPerl() && v3Global.opt.trace()) { + ofp()->putsPrivate(false); // public: + puts("static void\ttraceInit (SpTraceVcd* vcdp, void* userthis, uint32_t code);\n"); + puts("static void\ttraceFull (SpTraceVcd* vcdp, void* userthis, uint32_t code);\n"); + puts("static void\ttraceChg (SpTraceVcd* vcdp, void* userthis, uint32_t code);\n"); + } + + puts("} VL_ATTR_ALIGNED(8);\n"); + puts("\n"); + + // finish up h-file + if (!optSystemPerl()) { + puts("#endif /*guard*/\n"); + } +} + +//---------------------------------------------------------------------- + +void EmitCImp::emitImp(AstModule* modp) { + if (optSystemPerl()) { + puts("//############################################################\n"); + puts("#sp implementation\n"); + } + ofp()->printf("#include \"%-20s // For This\n", + (modClassName(modp)+".h\"").c_str()); + + // Us + if (modp->globalSyms() || modp->isTop() || 1 /*for thisp*/) { + puts("#include \""+ symClassName() +".h\"\n"); + puts("#define VlSym "+ symClassName() +"::s_thisp\n"); + } else { + // Instantiated modules + for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCell* cellp=nodep->castCell()) { + ofp()->printf("#include \"%-20s", + (modClassName(cellp->modp())+".h\"").c_str()); + puts(" // For "+cellp->name()+"\n"); + } + } + } + + if (optSystemPerl() && (m_splitFilenum || !m_fast)) { + puts("\n"); + puts("SP_MODULE_CONTINUED("+modClassName(modp)+");\n"); + } + + emitTextSection(AstType::SCIMPHDR); + if (m_fast && m_splitFilenum==0) { + emitTextSection(AstType::SCIMP); + emitStaticDecl(modp); + } + + // Constructor + if (m_slow && m_splitFilenum==0) { + puts("\n//--------------------\n"); + puts("\n"); + if (optSystemPerl() && modp->isTop()) { + puts("SP_CTOR_IMP("+modClassName(modp)+")"); + } else if (optSystemC() && modp->isTop()) { + puts("VL_SC_CTOR_IMP("+modClassName(modp)+")"); + } else { + puts("VL_CTOR_IMP("+modClassName(modp)+")"); + } + emitVarCtors(); + puts(" {\n"); + + emitCellCtors(modp); + emitSensitives(); + emitVarResets(modp); + emitCoverageCtor(modp); + emitTextSection(AstType::SCCTOR); + if (optSystemPerl()) puts("SP_AUTO_CTOR;\n"); + puts("}\n"); + } + if (m_fast && m_splitFilenum==0) { + if (modp->isTop()) { + emitStaticDecl(modp); + puts("\n//--------------------\n"); + puts("\n"); + emitWrapEval(modp); + } + } + + if (m_fast && m_splitFilenum==0) { + if (v3Global.opt.trace() && optSystemC() && m_modp->isTop()) { + puts("\n"); + puts("\n/*AUTOTRACE(__MODULE__,recurse,activity,exists)*/\n\n"); + } + } + + // Blocks + puts("\n//--------------------\n"); + puts("// Internal Methods\n"); +} + +void EmitCImp::emitImpBottom(AstModule* modp) { + if (modp->globalSyms()) { + puts("#undef VlSym\n"); + } +} + +//###################################################################### + +void EmitCImp::main(AstModule* modp, bool slow, bool fast) { + // Output a module + m_modp = modp; + m_slow = slow; + m_fast = fast; + string filenameNoExt = v3Global.opt.makeDir()+"/"+ modClassName(modp)+(m_fast ? "" : "__Slow"); + + if (debug()>=5) { + for (int i=0;ilevel();i++) { cout<<" "; } + UINFONL(0," Emitting "<stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCFunc* funcp = nodep->castCFunc()) { + if (v3Global.opt.outputSplit() > 1 && splitSize() + && v3Global.opt.outputSplit() < splitSize()) { + // Close old file + emitImpBottom (modp); + delete m_ofp; m_ofp=NULL; + + // Open a new file + m_splitSize = 0; + m_ofp = newOutCFile (modp, !m_fast, true/*source*/, ++m_splitFilenum); + emitImp (modp); + } + mainDoFunc(funcp); + } + } + + emitImpBottom (modp); + delete m_ofp; m_ofp=NULL; +} + +//###################################################################### +// Tracing routines + +class EmitCTrace : EmitCStmts { + AstCFunc* m_funcp; // Function we're in now + bool m_slow; // Making slow file + // METHODS + void emitTraceHeader() { + // Includes + if (optSystemC()) { + puts("#include \"SpTraceVcd.h\"\n"); + } + puts("#include \"SpTraceVcdC.h\"\n"); + puts("#include \""+ symClassName() +".h\"\n"); + puts("#define VlSym "+ symClassName() +"::s_thisp\n"); + puts("\n"); + } + + void emitTraceSlow() { + puts("\n//======================\n\n"); + + puts("void "+topClassName()+"::trace ("); + if (optSystemC()) { + puts("SpTraceFile* tfp, int, int) {\n"); + } else { + puts("SpTraceVcdCFile* tfp, int, int) {\n"); + } + puts( "tfp->spTrace()->addCallback (" + "&"+topClassName()+"::traceInit" + +", &"+topClassName()+"::traceFull" + +", &"+topClassName()+"::traceChg, this);\n"); + puts("}\n"); + + puts("void "+topClassName()+"::traceInit(SpTraceVcd* vcdp, void* userthis, uint32_t code) {\n"); + puts("// Callback from vcd->open()\n"); + puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); + puts(symClassName()+"::init(t); // Setup global symbol table\n"); + puts("if (!Verilated::calcUnusedSigs()) vl_fatal(__FILE__,__LINE__,__FILE__,\"Turning on wave traces requires Verilated::traceEverOn(true) call before time 0.\");\n"); + puts("t->traceInitThis (vcdp, code);\n"); + puts("}\n"); + + puts("void "+topClassName()+"::traceFull(SpTraceVcd* vcdp, void* userthis, uint32_t code) {\n"); + puts("// Callback from vcd->dump()\n"); + puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); + puts(symClassName()+"::init(t); // Setup global symbol table\n"); + puts("t->traceFullThis (vcdp, code);\n"); + puts("}\n"); + + puts("\n//======================\n\n"); + } + + void emitTraceFast() { + puts("\n//======================\n\n"); + + puts("void "+topClassName()+"::traceChg(SpTraceVcd* vcdp, void* userthis, uint32_t code) {\n"); + puts("// Callback from vcd->dump()\n"); + puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); + puts("if (t->getClearActivity()) {\n"); + puts(symClassName()+"::init(t); // Setup global symbol table\n"); + puts("t->traceChgThis (vcdp, code);\n"); + puts("}\n"); + puts("}\n"); + + puts("\n//======================\n\n"); + } + + bool emitTraceIsScWide(AstTraceInc* nodep) { + AstVarRef* varrefp = nodep->valuep()->castVarRef(); + if (!varrefp) return false; + AstVar* varp = varrefp->varp(); + return varp->isSc() && varp->isScWide(); + } + void emitTraceInitOne(AstTraceDecl* nodep) { + if (nodep->isWide()) { + puts("vcdp->declArray"); + } else if (nodep->isQuad()) { + puts("vcdp->declQuad "); + } else if (nodep->msb() || nodep->lsb()) { + puts("vcdp->declBus "); + } else { + puts("vcdp->declBit "); + } + puts("(c+"+cvtToStr(nodep->code())); + if (nodep->arrayWidth()) puts("+i*"+cvtToStr(nodep->widthWords())); + puts(",\""+nodep->showname()+"\""); + if (nodep->arrayWidth()) { + puts(",(i+"+cvtToStr(nodep->arrayLsb())+")"); + } else { + puts(",-1"); + } + if (nodep->msb() || nodep->lsb()) { + puts(","+cvtToStr(nodep->msb())+","+cvtToStr(nodep->lsb())); + } + puts(");"); + } + + void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) { + nodep->precondsp()->iterateAndNext(*this); + string full = (m_funcp->funcType() == AstCFuncType::TRACE_FULL) ? "full":"chg"; + if (nodep->isWide() || emitTraceIsScWide(nodep)) { + puts("vcdp->"+full+"Array"); + } else if (nodep->isQuad()) { + puts("vcdp->"+full+"Quad "); + } else if (nodep->declp()->msb() || nodep->declp()->lsb()) { + puts("vcdp->"+full+"Bus "); + } else { + puts("vcdp->"+full+"Bit "); + } + puts("(c+"+cvtToStr(nodep->declp()->code() + + ((arrayindex<0) ? 0 : (arrayindex*nodep->declp()->widthWords())))); + puts(","); + emitTraceValue(nodep, arrayindex); + if (nodep->declp()->msb() || nodep->declp()->lsb()) { + puts(","+cvtToStr(nodep->declp()->widthMin())); + } + puts(");\n"); + } + void emitTraceValue(AstTraceInc* nodep, int arrayindex) { + if (nodep->valuep()->castVarRef()) { + AstVarRef* varrefp = nodep->valuep()->castVarRef(); + AstVar* varp = varrefp->varp(); + if (emitTraceIsScWide(nodep)) puts("(uint32_t*)"); + puts("("); + varrefp->iterate(*this); // Put var name out + if (varp->arraysp()) { + if (arrayindex==-2) puts("[i]"); + else if (arrayindex==-1) puts("[0]"); + else puts("["+cvtToStr(arrayindex)+"]"); + } + if (varp->isSc()) puts(".read()"); + if (emitTraceIsScWide(nodep)) puts(".get_datap()"); + puts(")"); + } else { + puts("("); + nodep->valuep()->iterate(*this); + puts(")"); + } + } + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + // Top module only + nodep->topModulep()->accept(*this); + } + virtual void visit(AstModule* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + virtual void visit(AstCFunc* nodep, AstNUser*) { + if (nodep->slow() != m_slow) return; + if (nodep->funcType() == AstCFuncType::TRACE_INIT + || nodep->funcType() == AstCFuncType::TRACE_FULL + || nodep->funcType() == AstCFuncType::TRACE_CHANGE) { + m_funcp = nodep; + + puts("\n"); + puts(nodep->rtnTypeVoid()); puts(" "); + puts(topClassName()+"::"+nodep->name() + +"("+cFuncArgs(nodep)+") {\n"); + + puts("int c=code;\n"); + puts("if (0 && vcdp && c) {} // Prevent unused\n"); + if (nodep->funcType() == AstCFuncType::TRACE_INIT) { + puts("vcdp->module(name()); // Setup signal names\n"); + } else if (nodep->funcType() == AstCFuncType::TRACE_FULL) { + } else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) { + } else nodep->v3fatalSrc("Bad Case"); + + if (nodep->initsp()) puts("// Variables\n"); + emitVarList(nodep->initsp(), EVL_ALL, ""); + nodep->initsp()->iterateAndNext(*this); + ofp()->putAlign(4); + + puts("// Body\n"); + puts("{\n"); + // Do the statements Note not from this node, but the TRACE_INIT's. + // That saved us from having 3 copies of all of the TRACEs + nodep->stmtsp()->iterateAndNext(*this); + puts("}\n"); + if (nodep->finalsp()) puts("// Final\n"); + nodep->finalsp()->iterateAndNext(*this); + puts("}\n"); + } + m_funcp = NULL; + } + virtual void visit(AstTraceDecl* nodep, AstNUser*) { + if (nodep->arrayWidth()) { + puts("{int i; for (i=0; i<"+cvtToStr(nodep->arrayWidth())+"; i++) {\n"); + emitTraceInitOne(nodep); + puts("}}\n"); + } else { + emitTraceInitOne(nodep); + puts("\n"); + } + } + virtual void visit(AstTraceInc* nodep, AstNUser*) { + if (nodep->declp()->arrayWidth()) { + // It traces faster if we unroll the loop + for (unsigned i=0; ideclp()->arrayWidth(); i++) { + emitTraceChangeOne(nodep, i); + } + } else { + emitTraceChangeOne(nodep, -1); + } + } + virtual void visit(AstCoverDecl* nodep, AstNUser*) { + } + virtual void visit(AstCoverInc* nodep, AstNUser*) { + } + +public: + EmitCTrace(bool slow) { + m_funcp = NULL; + m_slow = slow; + } + virtual ~EmitCTrace() {} + void main() { + // Put out the file + string filename = (v3Global.opt.makeDir()+"/"+ topClassName() + + (m_slow?"__Trace__Slow.cpp":"__Trace.cpp")); + AstCFile* cfilep = newCFile(filename, m_slow, true/*source*/); + cfilep->support(true); + + V3OutCFile of (filename); + of.putsHeader(); + m_ofp = &of; + + emitTraceHeader(); + + if (m_slow) emitTraceSlow(); + else emitTraceFast(); + + v3Global.rootp()->accept(*this); + + puts("#undef VlSym\n"); + m_ofp = NULL; + } +}; + +//###################################################################### +// EmitC class functions + +void V3EmitC::emitc() { + UINFO(2,__FUNCTION__<<": "<modulesp(); nodep; nodep=nodep->nextp()->castModule()) { + if (v3Global.opt.outputSplit()) { + { EmitCImp imp; imp.main(nodep, false, true); } + { EmitCImp imp; imp.main(nodep, true, false); } + } else { + { EmitCImp imp; imp.main(nodep, true, true); } + } + } +} + +void V3EmitC::emitcTrace() { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include + +#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 diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp new file mode 100644 index 000000000..7264c6889 --- /dev/null +++ b/src/V3EmitCSyms.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 ScopeModPair; + vector 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+": "<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::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::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::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::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__<<": "< +#include +#include +#include +#include +#include +#include + +#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__<<": "< +#include +#include +#include +#include +#include +#include + +#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: %"<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: "<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__<<": "<modulesp(); modp; modp=modp->nextp()->castModule()) { + EmitVImp imp; + V3OutVFile of (v3Global.opt.makeDir()+"/"+imp.modClassName(modp)+"__Vout.v"); + of.putsHeader(); + imp.main(modp, &of); + } + } +} diff --git a/src/V3EmitV.h b/src/V3EmitV.h new file mode 100644 index 000000000..a84764a77 --- /dev/null +++ b/src/V3EmitV.h @@ -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 diff --git a/src/V3Error.cpp b/src/V3Error.cpp new file mode 100644 index 000000000..a1c677d0d --- /dev/null +++ b/src/V3Error.cpp @@ -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 +#include +#include +#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; codeilineno(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 <ascii()<<": "<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 "<=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<=V3ErrorCode::FIRST_WARN && !s_describedWarnings) { + cerr<dumpTreeFile(v3Global.debugFilename("final.tree",99)); + V3Stats::statsFinalAll(v3Global.rootp()); + V3Stats::statsReport(); + } +#endif + } + abort(); +// exit(10); + } + } + } +} diff --git a/src/V3Error.h b/src/V3Error.h new file mode 100644 index 000000000..34a4bcdcd --- /dev/null +++ b/src/V3Error.h @@ -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 +#include +#include +#include + +//###################################################################### + +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(_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<=(level)) { cout<<"- "<=(level)) { cout< std::string cvtToStr (const T& t) { + ostringstream os; os< 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 diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp new file mode 100644 index 000000000..81eb77a35 --- /dev/null +++ b/src/V3Expand.cpp @@ -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 +#include +#include +#include + +#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 && wordwidthWords()) { + 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) "< {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; wwidthWords(); 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) "<widthWords(); w++) { + addWordAssign(nodep, w, newAstWordSelClone (rhsp, w)); + } + return true; + } + bool expandWide (AstNodeAssign* nodep, AstArraySel* rhsp) { + UINFO(8," Wordize ASSIGN(ARRAYSEL) "<widthWords(); w++) { + addWordAssign(nodep, w, newAstWordSelClone (rhsp, w)); + } + return true; + } + bool expandWide (AstNodeAssign* nodep, AstNot* rhsp) { + UINFO(8," Wordize ASSIGN(NOT) "< {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }} + for (int w=0; wwidthWords(); 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) "<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) "<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) "<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) "<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) "<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) "<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) "<lhsp()->widthWords(); w++) { + addWordAssign(nodep, w, newAstWordSelClone (rhsp->lhsp(), w)); + } + for (; wwidthWords(); 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) "<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 "<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) "<widthWords(); w++) { + addWordAssign(nodep, w, newAstWordSelClone (rhsp->fromp(), w + VL_BITWORD_I(lsb))); + } + return true; + } else { + UINFO(8," Wordize ASSIGN(EXTRACT,misalign) "<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) "<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) "<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) "<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 -- "<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 "<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) "<rhsp()->widthMin(); + for (int w=0; wwidthWords(); 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) "<fileline(), + lhsp); + } else { + UINFO(8," REPLICATE "<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; repnumfileline(), + 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) "<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; wwidthWords(); 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; repnumfileline(), + 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 "< (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} + AstNode* newp = NULL; + for (int w=0; wlhsp()->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 "< (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} + AstNode* newp = NULL; + for (int w=0; wlhsp()->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 "< (0!={or{for each_word{WORDSEL(lhs,#)}}} + AstNode* newp = NULL; + for (int w=0; wlhsp()->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 "<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 "< (0!={and{for each_word{WORDSEL(lhs,#)}}} + AstNode* newp = NULL; + for (int w=0; wlhsp()->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 "<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 "< (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}} + AstNode* newp = NULL; + for (int w=0; wlhsp()->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__<<": "< +#include +#include +#include +#include + +#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: "< m_filenameSet; // Files generated (elim duplicates) + set 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 ofp (V3File::new_ofstream(filename)); + if (ofp->fail()) v3fatalSrc("Can't write "<::iterator iter=m_filenameList.begin(); + iter!=m_filenameList.end(); ++iter) { + if (iter->target()) { + *ofp<filename()<<" "; + } + } + *ofp<<" : "; + *ofp<::iterator iter=m_filenameList.begin(); + iter!=m_filenameList.end(); ++iter) { + if (!iter->target()) { + *ofp<filename()<<" "; + } + } + *ofp< ofp (V3File::new_ofstream(filename)); + if (ofp->fail()) v3fatalSrc("Can't write "<::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<<" "<mtime(); + *ofp<<" \""<filename()<<"\""; + *ofp< ifp (V3File::new_ifstream_nodepend(filename)); + if (ifp->fail()) { + UINFO(2," --check-times failed: no input "<>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="<= chkSize + && chkStat.st_mtime >= chkMtime + && chkStat.st_mtime <= (chkMtime + 20))) { + UINFO(2," --check-times failed: out-of-date "<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); +} + diff --git a/src/V3File.h b/src/V3File.h new file mode 100644 index 000000000..00465adf5 --- /dev/null +++ b/src/V3File.h @@ -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 +#include +#include +#include + +//============================================================================ +// 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 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 diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp new file mode 100644 index 000000000..a467bae83 --- /dev/null +++ b/src/V3Gate.cpp @@ -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 +#include +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Gate.h" +#include "V3Ast.h" +#include "V3Graph.h" +#include "V3Const.h" +#include "V3Stats.h" + +typedef list 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 "<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 "<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: "<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 "<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 "<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 "<iterateChildren(*this); + m_scopep = NULL; + } + virtual void visit(AstActive* nodep, AstNUser*) { + // Create required blocks and add to module + UINFO(4," BLOCK "<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 "<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(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 "<varScp()<varScp()->varp()->v3warn(UNDRIVEN,"Signal has no drivers " + <scopep()->prettyName()<<"." + <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 "<name()<name()< + (vvertexp->inBeginp()->fromp()); + UINFO(8, " From "<name()<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(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"<outBeginp()<<" on"<<(vvertexp->outBeginp()?vvertexp->outBeginp()->outNextp():0) + <<" "<name() + <outBeginp(); edgep; edgep = edgep->outNextp()) { + GateLogicVertex* consumeVertexp = dynamic_cast(edgep->top()); + UINFO(9, " edge "<nodep()<inBeginp(); edgep; edgep = edgep->inNextp()) { + GateLogicVertex* consumeVertexp = dynamic_cast(edgep->fromp()); + UINFO(9, " edge "<nodep()<=5) logicp->dumpTree(cout,"\telimVar: "); + if (debug()>=5) substp->dumpTree(cout,"\t subst: "); + m_statSigs++; + while (V3GraphEdge* edgep = vvertexp->outBeginp()) { + GateLogicVertex* consumeVertexp = dynamic_cast(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 "<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(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<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(vertexp)) { + if (!vvertexp->consumed() && !vvertexp->user()) { + UINFO(8, "Unconsumed "<varScp()<(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 "<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 "<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__<<": "< +#include +#include + +#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 "<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 "<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 "<circular(true); + } + } + virtual void visit(AstNodeAssign* nodep, AstNUser*) { + //UINFO(8,"ASS "<iterateChildren(*this); + m_assignp = NULL; + } + virtual void visit(AstActive* nodep, AstNUser*) { + UINFO(8,"ACTIVE "<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__<<": "< + +#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 + diff --git a/src/V3Graph.cpp b/src/V3Graph.cpp new file mode 100644 index 000000000..dfaf293ae --- /dev/null +++ b/src/V3Graph.cpp @@ -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 +#include +#include +#include +#include +#include +#include + +#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="<name(); + if (vertexp->rank()) os<<" r"<rank(); + if (vertexp->fanout()) os<<" f"<fanout(); + if (vertexp->color()) os<<" c"<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)<<" "<verticesNextp()) { + os<<"\tNode: "<name(); + if (vertexp->color()) os<<" color="<color(); + os<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 << "-> "<top()->name(); + if (edgep->top() == vertexp) os << "<- "<fromp()->name(); + if (edgep->cutable()) os<<" [CUTABLE]"; + os< logp (V3File::new_ofstream(filename)); + if (logp->fail()) v3fatalSrc("Can't write "< 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 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_"<dotName()<<(n++) + <<"\t[fontsize=8 " + <<"label=\""<<(vertexp->name()!="" ? vertexp->name() : "\\N"); + if (vertexp->rank()) *logp<<" r"<rank(); + if (vertexp->fanout()) *logp<<" f"<fanout(); + if (vertexp->color()) *logp<<"\\n c"<color(); + *logp<<"\""; + *logp<<", color="<dotColor(); + if (vertexp->dotStyle()!="") *logp<<", style="<dotStyle(); + if (vertexp->dotShape()!="") *logp<<", shape="<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"<fromp()->dotName()< n"<top()->dotName()<name()!="" ? edgep->name() : "\\E")<<"\"" + <<"fontsize=8 label=\""<<(edgep->dotLabel()!="" ? edgep->dotLabel() : "")<<"\"" + <<" weight="<weight() + <<" color="<dotColor(); + if (edgep->dotStyle()!="") *logp<<" style="<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 "< +#include + +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 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: "< m_vertices;// All vertices, linked list + V3List m_outs; // Outbound edges,linked list + V3List 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 m_outs; // Next Outbound edge for same vertex (linked list) + V3ListEnt 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 diff --git a/src/V3GraphAcyc.cpp b/src/V3GraphAcyc.cpp new file mode 100644 index 000000000..673277b3f --- /dev/null +++ b/src/V3GraphAcyc.cpp @@ -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 +#include +#include +#include +#include +#include + +#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 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 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 "<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 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 m_work; // List of vertices with optimization work left + vector 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 "<fromp()<cut(); + OrigEdgeList* oEListp = (OrigEdgeList*)(breakEdgep->userp()); + if (!oEListp) v3fatalSrc("No original edge associated with cutting edge "<begin(); it != oEListp->end(); ++it) { + V3GraphEdge* origEdgep = *it; + origEdgep->cut(); + UINFO(8," "<fromp()<<" ->"<top()<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::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 "<setDelete(); // Mark so we won't delete it twice + // Remove edges + while (V3GraphEdge* edgep = avertexp->outBeginp()) { + V3GraphVertex* otherVertexp = edgep->top(); + //UINFO(9," out "<unlinkDelete(); edgep = NULL; + workPush(otherVertexp); + } + while (V3GraphEdge* edgep = avertexp->inBeginp()) { + V3GraphVertex* otherVertexp = edgep->fromp(); + //UINFO(9," in "<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 "<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()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 "<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 "<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 "<unlinkDelete(); edgep = NULL; + } else if (!edgep->cutable()) { + // !cutable duplicates prev cutable: delete the earlier cutable + UINFO(8," DelDupPrev "<unlinkDelete(); prevEdgep = NULL; + outVertexp->userp(edgep); + } else { + // cutable duplicates prev cutable: combine weights + UINFO(8," DelDupComb "<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 = "< 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::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"<weight()<<" "<fromp()<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"); +} diff --git a/src/V3GraphAlg.cpp b/src/V3GraphAlg.cpp new file mode 100644 index 000000000..74095c72c --- /dev/null +++ b/src/V3GraphAlg.cpp @@ -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 +#include +#include +#include +#include +#include +#include + +#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 "<name()<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 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 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; iloopsVertexCb(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)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 vertices; + for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { + vertices.push_back(vertexp); + } + sort(vertices.begin(), vertices.end(), GraphOrderVertexCmp()); + this->verticesUnlink(); + for (vector::iterator it = vertices.begin(); it!=vertices.end(); ++it) { + (*it)->verticesPushBack(this); + } + } + + // Sort edges by rank then fanout of node they point to + vector 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::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(); +} diff --git a/src/V3GraphAlg.h b/src/V3GraphAlg.h new file mode 100644 index 000000000..7d4cabd9e --- /dev/null +++ b/src/V3GraphAlg.h @@ -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 diff --git a/src/V3GraphDfa.cpp b/src/V3GraphDfa.cpp new file mode 100644 index 000000000..97225a511 --- /dev/null +++ b/src/V3GraphDfa.cpp @@ -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 +#include +#include +#include +#include +#include + +#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(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 DfaStates; + typedef multimap 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(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 "<outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { + if (nfaState(dfaEdgep->top())) { + DfaVertex* nfaStatep = static_cast(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 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: "<outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { + if (nfaState(dfaEdgep->top())) { + DfaVertex* nfaStatep = static_cast(dfaEdgep->top()); + // Foreach input transition (on this nfaStatep) + for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) { + DfaEdge* cNfaEdgep = static_cast(nfaEdgep); + if (cNfaEdgep->input() == input) { + DfaVertex* nextStatep = static_cast(cNfaEdgep->top()); + if (unseenNfaThisStep(nextStatep)) { // Not processed? + nfasWithInput.push_back(nextStatep); + nextStatep->user(m_step); + UINFO(9," Reachable "<outBeginp(); nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) { + DfaEdge* cNfaEdgep = static_cast(nfaEdgep); + if (cNfaEdgep->epsilon()) { + DfaVertex* nextStatep = static_cast(cNfaEdgep->top()); + if (unseenNfaThisStep(nextStatep)) { // Not processed? + nfasWithInput.push_back(nextStatep); + nextStatep->user(m_step); + UINFO(9," Epsilon Reachable "<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 "<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(nfaEdgep); + DfaVertex* nfaStatep = static_cast(nfaEdgep->top()); + //UINFO(9," Consider "<top()<<" EP "<epsilon()<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 "< 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(dfaEdgep->top()); + // Foreach input on this nfaStatep + for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) { + DfaEdge* cNfaEdgep = static_cast(nfaEdgep); + if (!cNfaEdgep->epsilon()) { + if (inputs.find(cNfaEdgep->input()) == inputs.end()) { + inputs.insert(cNfaEdgep->input()); + UINFO(9," Input to "<input())<<" via "<::const_iterator inIt=inputs.begin(); inIt!=inputs.end(); ++inIt) { + DfaInput input = *inIt; + UINFO(9," ==="<<++i<<"=======================\n"); + UINFO(9," On input "<<(void*)(input)<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(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(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 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 workps; + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { + if (DfaVertex* vvertexp = dynamic_cast(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(edgep->fromp()); + if (fromvertexp != vertexp + && !fromvertexp->user()) { + workps.push(static_cast(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); +} diff --git a/src/V3GraphDfa.h b/src/V3GraphDfa.h new file mode 100644 index 000000000..0f80f00b4 --- /dev/null +++ b/src/V3GraphDfa.h @@ -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 + +#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 diff --git a/src/V3GraphTest.cpp b/src/V3GraphTest.cpp new file mode 100644 index 000000000..5ae24b0a1 --- /dev/null +++ b/src/V3GraphTest.cpp @@ -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 +#include +#include + +#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_dlyacyclic(&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__<<": "< +#include +#include +#include + +#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 "<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 "<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 "<user4()<<" "<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 "<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 "<user4p()) nodep->v3fatalSrc("Called findDuplicate on non-hashed node"); + pair 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(); +} diff --git a/src/V3Hashed.h b/src/V3Hashed.h new file mode 100644 index 000000000..87d532f88 --- /dev/null +++ b/src/V3Hashed.h @@ -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 + +//============================================================================ + +class V3Hashed { + // NODE STATE + // AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal) + + // TYPES + typedef multimap 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 diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp new file mode 100644 index 000000000..4064d1784 --- /dev/null +++ b/src/V3Inline.cpp @@ -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 +#include +#include +#include +#include + +#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 "<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 "<=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 "<modVarp()<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 "<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," "<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," "<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: "<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="<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 "<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__<<": "<modulesp(); modp; modp=nextmodp) { + nextmodp = modp->nextp()->castModule(); + if (modp->userp()) { // Was inlined + modp->unlinkFrBack()->deleteTree(); modp=NULL; + } + } +} diff --git a/src/V3Inline.h b/src/V3Inline.h new file mode 100644 index 000000000..19e98c645 --- /dev/null +++ b/src/V3Inline.h @@ -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 diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp new file mode 100644 index 000000000..041e39bf7 --- /dev/null +++ b/src/V3Inst.cpp @@ -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 +#include +#include +#include + +#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 "<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 "<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 "<=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 "<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<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 "<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__<<": "< + +//============================================================================ + +class V3LanguageWords { + // List of common reserved keywords + + private: + map 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::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 + + diff --git a/src/V3Life.cpp b/src/V3Life.cpp new file mode 100644 index 000000000..11f4c8195 --- /dev/null +++ b/src/V3Life.cpp @@ -0,0 +1,295 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Lifelicate variable assignment 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. +// +//************************************************************************* +// LIFE TRANSFORMATIONS: +// Build control-flow graph with assignments and var usages +// All modules: +// ASSIGN(x,...), ASSIGN(x,...) => delete first one +// We also track across if statements: +// ASSIGN(X,...) IF( ..., ASSIGN(X,...), ASSIGN(X,...)) => deletes first +// We don't do the opposite yet though (remove assigns in if followed by outside if) +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Life.h" +#include "V3Stats.h" +#include "V3Ast.h" + +//###################################################################### +// Life state, as a visitor of each AstNode + +class LifeVarEntry { + AstNode* m_assignp; // Last assignment to this varscope + bool m_firstSet; // True if creation was a set (and thus block above may have a set that can be deleted +public: + LifeVarEntry() { // Construction for when a var is used + clearAssign(); + m_firstSet = false; + } + LifeVarEntry(AstNode* assp) { // Construction for when a var is set + m_assignp = assp; + m_firstSet = true; + } + LifeVarEntry(bool) { // Construction for when a var is set, but ass isn't + m_assignp = NULL; + m_firstSet = true; + } + AstNode* assignp() const { return m_assignp; } + bool firstSet() const { return m_firstSet; } + void clearAssign() { m_assignp = NULL; } + void newerAssign(AstNode* assp) { m_assignp = assp; } +}; + +class LifeVisitor : public AstNVisitor { +private: + // NODE STATE + // AstVarScope::user() -> int. Used in combining to detect duplicates + + // STATE + //static int debug() { return 9; } + int m_ifCount; // If counter + V3Double0 m_statAssnDel; // Statistic tracking + + // LIFE MAP + // For each basic block, we'll make a new map of what variables that if/else is changing + typedef std::map LifeMap; + LifeMap *m_lifep; // Current active lifetime map for current scope + + // METHODS + void checkRemoveAssign(LifeVarEntry* entp) { + // Check the var entry, and remove if appropriate + if (AstNode* oldassp = entp->assignp()) { + UINFO(7," PREV: "<4) oldassp->dumpTree(cout, " REMOVE/SAMEBLK "); + entp->clearAssign(); + oldassp->unlinkFrBack(); + pushDeletep(oldassp); + m_statAssnDel++; + } + } + void setLast(AstVarScope* nodep, AstNode* assp) { + // Do we have a old assignment we can nuke? + UINFO(4," ASSIGNof: "<find(nodep); + if (iter != m_lifep->end()) { + checkRemoveAssign(&(iter->second)); + iter->second.newerAssign(assp); + } else { + m_lifep->insert(make_pair(nodep,LifeVarEntry(assp))); + } + //lifeDump(); + } + void clearLastAssign(AstVarScope* nodep) { + UINFO(4," clearof: "<find(nodep); + if (iter != m_lifep->end()) { + iter->second.clearAssign(); + } else { + m_lifep->insert(make_pair(nodep,LifeVarEntry())); + } + } + void lifeDump(LifeMap* lifep) { + UINFO(5, " LifeMap:"<begin(); it!=lifep->end(); ++it) { + UINFO(5, " Ent: " + <<(it->second.firstSet()?"[F] ":" ") + <first<second.assignp()) { + UINFO(5, " Ass: "<second.assignp()<begin(); it!=sublifep->end(); ++it) { + clearLastAssign(it->first); + } + } + void dualBranch (LifeMap* life1p, LifeMap* life2p) { + // Find any common sets on both branches of IF and propagate upwards + //lifeDump(life1p); + //lifeDump(life2p); + m_ifCount++; + for (LifeMap::iterator it = life1p->begin(); it!=life1p->end(); ++it) { + // When the if branch sets a var before it's used, mark that variable + if (it->second.firstSet()) it->first->user(m_ifCount); + } + for (LifeMap::iterator it = life2p->begin(); it!=life2p->end(); ++it) { + // When the else branch sets a var before it's used + AstVarScope* nodep = it->first; + if (it->second.firstSet() + && nodep->user()==m_ifCount) { // And the if hit the same var + UINFO(4,"DUALBRANCH "<find(nodep); + if (iter != m_lifep->end()) { + checkRemoveAssign(&(iter->second)); + iter->second.clearAssign(); + } else { + m_lifep->insert(make_pair(nodep,LifeVarEntry(true))); + } + } + } + //lifeDump(m_lifep); + } + + // VISITORS + virtual void visit(AstModule* nodep, AstNUser*) { + // Only track the top scopes, not lower level functions + if (nodep->isTop()) nodep->iterateChildren(*this); + } + virtual void visit(AstTopScope* nodep, AstNUser*) { + AstNode::userClearTree(); // userp() used on entire tree + AstNode::user2ClearTree(); // userp() used on entire tree + AstNode::user3ClearTree(); // userp() used on entire tree + AstNode::user4ClearTree(); // userp() used on entire tree + + AstScope* scopep = nodep->scopep()->castScope(); + if (!scopep) nodep->v3fatalSrc("TopScope has no scope\n"); + AstCFunc* evalp = NULL; + for (AstNode* searchp = scopep->blocksp(); searchp; searchp=searchp->nextp()) { + if (AstCFunc* funcp = searchp->castCFunc()) { + if (funcp->name() == "_eval") { + evalp = funcp; + break; + } + } + } + if (!evalp) nodep->v3fatalSrc("Top _eval entry function not found\n"); + evalp->iterateChildren(*this); + } + + virtual void visit(AstVarRef* nodep, AstNUser*) { + // Consumption/generation of a variable, + // it's used so can't elim assignment before this use. + if (!nodep->varScopep()) nodep->v3fatalSrc("NULL"); + // + AstVarScope* vscp = nodep->varScopep(); + if (!vscp) nodep->v3fatalSrc("Scope not assigned"); + clearLastAssign(vscp); + } + virtual void visit(AstNodeAssign* nodep, AstNUser*) { + // Collect any used variables first, as lhs may also be on rhs + nodep->rhsp()->iterateAndNext(*this); + // Has to be direct assignment without any EXTRACTing. + if (nodep->lhsp()->castVarRef()) { + AstVarScope* vscp = nodep->lhsp()->castVarRef()->varScopep(); + if (!vscp) vscp->v3fatalSrc("Scope lost on variable"); + setLast(vscp, nodep); + } else { + nodep->lhsp()->iterateAndNext(*this); + } + } + + //---- Track control flow changes + virtual void visit(AstNodeIf* nodep, AstNUser*) { + UINFO(4," IF "<condp()->iterateAndNext(*this); + LifeMap* prevLifeMapp = m_lifep; + LifeMap* ifLifep = new LifeMap; + LifeMap* elseLifep = new LifeMap; + { + m_lifep = ifLifep; + nodep->ifsp()->iterateAndNext(*this); + } + { + m_lifep = elseLifep; + nodep->elsesp()->iterateAndNext(*this); + } + UINFO(4," join "<precondsp()->iterateAndNext(*this); + nodep->condp()->iterateAndNext(*this); + } + { + m_lifep = bodyLifep; + nodep->bodysp()->iterateAndNext(*this); + } + UINFO(4," joinfor"<iterateChildren(*this); + // Enter the function and trace it + nodep->funcp()->accept(*this); + } + virtual void visit(AstVar*, AstNUser*) {} // Don't want varrefs under it + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + +public: + // CONSTRUCTORS + LifeVisitor(AstNetlist* nodep) { + m_ifCount = 1; + m_lifep = new LifeMap; + nodep->accept(*this); + delete m_lifep; + } + virtual ~LifeVisitor() { + V3Stats::addStat("Optimizations, Lifetime assign deletions", m_statAssnDel); + } +}; + +//###################################################################### +// Life class functions + +void V3Life::lifeAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include + +#include "V3Global.h" +#include "V3LifePost.h" +#include "V3Stats.h" +#include "V3Ast.h" + +//###################################################################### +// LifePost state, as a visitor of each AstNode + +class LifePostBaseVisitor : public AstNVisitor { +protected: +// static int debug() { return 9; } +}; + +//###################################################################### +// LifePost class functions + +class LifePostElimVisitor : public LifePostBaseVisitor { +private: + // NODE STATE + // INPUT: + // AstVarScope::user4p() -> AstVarScope*, If set, replace this varscope with specified new one + // STATE + // VISITORS + virtual void visit(AstVarRef* nodep, AstNUser*) { + AstVarScope* vscp = nodep->varScopep(); + if (!vscp) nodep->v3fatalSrc("Scope not assigned"); + if (AstVarScope* newvscp = (AstVarScope*)vscp->user4p()) { + UINFO(9, " Replace "<fileline(), newvscp, nodep->lvalue()); + nodep->replaceWith(newrefp); + nodep->deleteTree(); nodep=NULL; + } + } + virtual void visit(AstModule* nodep, AstNUser*) { + // Only track the top scopes, not lower level functions + if (nodep->isTop()) 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(AstVar*, AstNUser*) {} // Don't want varrefs under it + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTRUCTORS + LifePostElimVisitor(AstTopScope* nodep) { + nodep->accept(*this); + } + virtual ~LifePostElimVisitor() {} +}; + +//###################################################################### +// LifePostlicate delay elimination + +class LifePostDlyVisitor : public LifePostBaseVisitor { +private: + // NODE STATE + // Cleared on entire tree + // AstVarScope::user() -> Sequence # of first virtex setting this var. + // AstVarScope::user2() -> Sequence # of last consumption of this var + // AstVarScope::user4() -> AstVarScope*: Passed to LifePostElim to substitute this var + + // STATE + uint32_t m_sequence; // Sequence number of assignments/varrefs + V3Double0 m_statAssnDel; // Statistic tracking + + // VISITORS + virtual void visit(AstTopScope* nodep, AstNUser*) { + AstNode::userClearTree(); // userp() used on entire tree + AstNode::user2ClearTree(); // userp() used on entire tree + AstNode::user3ClearTree(); // userp() used on entire tree + AstNode::user4ClearTree(); // userp() used on entire tree + m_sequence = 0; + nodep->iterateChildren(*this); + + // Replace any node4p varscopes with the new scope + LifePostElimVisitor visitor (nodep); + } + + virtual void visit(AstVarRef* nodep, AstNUser*) { + // Consumption/generation of a variable, + AstVarScope* vscp = nodep->varScopep(); + if (!vscp) nodep->v3fatalSrc("Scope not assigned"); + m_sequence++; + if (nodep->lvalue()) { + // First generator + if (!vscp->user()) vscp->user(m_sequence); + } else { + // Last consumer + vscp->user2(m_sequence); + } + } + virtual void visit(AstAssignPost* nodep, AstNUser*) { + if (AstVarRef* lhsp = nodep->lhsp()->castVarRef()) { + if (AstVarRef* rhsp = nodep->rhsp()->castVarRef()) { + // Scrunch these: + // __Vdly__q = __PVT__clk_clocks; + // ... {no reads or writes of __PVT__q after the first write to __Vdly__q} + // ... {no reads of __Vdly__q after the first write to __Vdly__q} + // __PVT__q = __Vdly__q; + UINFO(9," POST "<varScopep()->user(); + uint32_t lastRead = rhsp->varScopep()->user2(); + uint32_t lastRead2 = lhsp->varScopep()->user2(); + UINFO(9," first "<varScopep()->user4p(lhsp->varScopep()); + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + m_statAssnDel++; + } + } + } + } + virtual void visit(AstModule* nodep, AstNUser*) { + // Only track the top scopes, not lower level functions + if (nodep->isTop()) 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(AstVar*, AstNUser*) {} // Don't want varrefs under it + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTRUCTORS + LifePostDlyVisitor(AstNetlist* nodep) { + nodep->accept(*this); + } + virtual ~LifePostDlyVisitor() { + V3Stats::addStat("Optimizations, Lifetime postassign deletions", m_statAssnDel); + } +}; + +//###################################################################### +// LifePost class functions + +void V3LifePost::lifepostAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Link.h" +#include "V3SymTable.h" +#include "V3Ast.h" + +//###################################################################### +// Link state, as a visitor of each AstNode + +class LinkVisitor : public AstNVisitor { +private: + // NODE STATE + // Entire netlist: + // AstModule::userp() // V3SymTable* Module's Symbol table + // AstNodeFTask::userp() // V3SymTable* Local Symbol table + // AstBegin::userp() // V3SymTable* Local Symbol table + // AstVar::userp() // V3SymTable* Table used to create this variable + + // ENUMS + enum IdState { // Which loop through the tree + ID_FIND, // Find all identifiers and add to lists + ID_PARAM, // Move parameters to cell definitions + ID_RESOLVE}; // Resolve all identifiers and report errors + + // STATE + // Below state needs to be preserved between each module call. + AstModule* m_modp; // Current module + IdState m_idState; // Id linking mode (find or resolve) + int m_paramNum; // Parameter number, for position based connection + V3SymTable* m_curVarsp; // Symbol table of variables and tasks under table we're inserting into + V3SymTable* m_cellVarsp; // Symbol table of variables under cell's module + vector m_delSymps; // Symbol tables to delete + + //int debug() { return 9; } + + // METHODS + void linkVarName (AstVarRef* nodep) { + if (!nodep->varp()) { + AstVar* varp = m_curVarsp->findIdName(nodep->name())->castVar(); + nodep->varp(varp); + } + } + + const char* varTextType(AstNode* nodep) { + const char* what = "node"; + if (nodep->castVar()) what = "variable"; + else if (nodep->castCell()) what = "cell"; + else if (nodep->castTask()) what = "task"; + else if (nodep->castFunc()) what = "function"; + return what; + } + + void createImplicitVar (AstVarRef* forrefp, bool noWarn) { + // Create implicit after warning + linkVarName(forrefp); + if (!forrefp->varp()) { + if (!noWarn) forrefp->v3warn(IMPLICIT,"Signal definition not found, creating implicitly: "<prettyName()); + AstVar* newp = new AstVar (forrefp->fileline(), AstVarType::WIRE, + forrefp->name()); + newp->trace(m_modp->modTrace()); + m_modp->addStmtp(newp); + // Link it to signal list + IdState old_id = m_idState; + m_idState = ID_FIND; + newp->accept(*this); + m_idState = old_id; + } + } + + // VISITs + virtual void visit(AstNetlist* nodep, AstNUser*) { + AstNode::userClearTree(); + // Look at all modules, and store pointers to all module names + for (AstModule* modp = v3Global.rootp()->modulesp(); modp; modp=modp->nextp()->castModule()) { + V3SymTable* symp = new V3SymTable(NULL); + m_delSymps.push_back(symp); + modp->userp(symp); + } + // And recurse... + m_idState = ID_FIND; + nodep->iterateChildren(*this); + m_idState = ID_PARAM; + nodep->iterateChildren(*this); + m_idState = ID_RESOLVE; + nodep->iterateChildren(*this); + nodep->checkTree(); + } + + virtual void visit(AstModule* nodep, AstNUser*) { + // Module: Create sim table for entire module and iterate + UINFO(2,"Link Module: "<userp()->castSymTable(); + if (!m_curVarsp) nodep->v3fatalSrc("NULL"); + m_cellVarsp = NULL; + m_paramNum = 0; + nodep->iterateChildren(*this); + // Prep for next + m_curVarsp = NULL; + m_modp = NULL; + } + virtual void visit(AstVar* nodep, AstNUser*) { + // Var: Remember its name for later resolution + if (!m_curVarsp) nodep->v3fatalSrc("Var not under module??\n"); + nodep->iterateChildren(*this); + if (m_idState==ID_FIND) { + // Find under either a task or the module's vars + AstNode* findidp = m_curVarsp->findIdName(nodep->name()); + AstVar* findvarp = findidp->castVar(); + bool ins=false; + if (!findidp) { + ins=true; + } else if (!findvarp) { + nodep->v3error("Unsupported in C: Variable has same name as " + <prettyName()); + } else if (findvarp != nodep) { + UINFO(4,"DupVar: "<userp() == m_curVarsp) { // Only when on same level + if ((findvarp->isIO() && nodep->isSignal()) + || (findvarp->isSignal() && nodep->isIO())) { + findvarp->combineType(nodep); + nodep->unlinkFrBack(); nodep=NULL; + } else { + nodep->v3error("Duplicate declaration of signal."); + findvarp->v3error("... Location of original declaration"); + } + } else { + // User can disable the message at either point + if (!nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN) + && !findvarp->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) { + nodep->v3warn(VARHIDDEN,"Declaration of signal hides declaration in upper scope: "<name()); + findvarp->v3warn(VARHIDDEN,"... Location of original declaration"); + } + ins = true; + } + } + if (ins) { + m_curVarsp->insert(nodep->name(), nodep); + nodep->userp(m_curVarsp); + if (nodep->isGParam()) { + m_paramNum++; + m_curVarsp->insert("__paramNumber"+cvtToStr(m_paramNum), nodep); + } + } + } + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + // VarRef: Resolve its reference + if (m_idState==ID_RESOLVE && !nodep->varp()) { + linkVarName(nodep); + if (!nodep->varp()) { + nodep->v3error("Can't find definition of signal: "<prettyName()); + createImplicitVar (nodep, true); // So only complain once + } + } + nodep->iterateChildren(*this); + } + //virtual void visit(AstVarXRef* nodep, AstNUser*) { + // See LinkDotVisitor + //} + + virtual void visit(AstNodeFTask* nodep, AstNUser*) { + // NodeTask: Remember its name for later resolution + if (!m_curVarsp) nodep->v3fatalSrc("Function/Task not under module??\n"); + // Remember the existing symbol table scope + V3SymTable* upperVarsp = m_curVarsp; + { + // Create symbol table for the task's vars + if (V3SymTable* localVarsp = nodep->userp()->castSymTable()) { + m_curVarsp = localVarsp; + } else { + m_curVarsp = new V3SymTable(upperVarsp); + m_delSymps.push_back(m_curVarsp); + nodep->userp(m_curVarsp); + } + // Convert the func's range to the output variable + if (AstFunc* funcp = nodep->castFunc()) { + if (!funcp->fvarp()->castVar()) { + AstRange* rangep = funcp->fvarp()->castRange(); + if (rangep) rangep->unlinkFrBack(); + AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::OUTPUT, nodep->name(), rangep); + newvarp->isSigned(funcp->isSigned()); + newvarp->funcReturn(true); + newvarp->trace(false); // Not user visible + funcp->addFvarp(newvarp); + // Explicit insert required, as the var name shadows the upper level's task name + m_curVarsp->insert(newvarp->name(), newvarp); + } + } + nodep->iterateChildren(*this); + } + m_curVarsp = upperVarsp; + if (m_idState==ID_FIND) { + AstNode* findidp = m_curVarsp->findIdName(nodep->name()); + AstNodeFTask* findtaskp = findidp->castNodeFTask(); + if (!findidp) { + m_curVarsp->insert(nodep->name(), nodep); + } else if (!findtaskp) { + nodep->v3error("Unsupported in C: Task/function has same name as " + <prettyName()); + } else if (findtaskp!=nodep) { + nodep->v3error("Duplicate declaration of task/function: "<prettyName()); + } + } + } + virtual void visit(AstBegin* nodep, AstNUser*) { + // Link variables underneath blocks + // Remember the existing symbol table scope + V3SymTable* upperVarsp = m_curVarsp; + { + // Create symbol table for the task's vars + if (V3SymTable* localVarsp = nodep->userp()->castSymTable()) { + m_curVarsp = localVarsp; + } else { + m_curVarsp = new V3SymTable(upperVarsp); + m_delSymps.push_back(m_curVarsp); + nodep->userp(m_curVarsp); + } + nodep->iterateChildren(*this); + } + m_curVarsp = upperVarsp; + } + virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { + // NodeFTaskRef: Resolve its reference + if (m_idState==ID_RESOLVE && !nodep->taskp()) { + if (nodep->dotted() == "") { + AstNodeFTask* taskp = m_curVarsp->findIdName(nodep->name())->castNodeFTask(); + if (!taskp) { nodep->v3error("Can't find definition of task/function: "<prettyName()); } + nodep->taskp(taskp); + } + } + nodep->iterateChildren(*this); + } + + virtual void visit(AstCell* nodep, AstNUser*) { + // Cell: Resolve its filename. If necessary, parse it. + if (m_idState==ID_FIND) { + // Add to list of all cells, for error checking and defparam's + AstNode* findidp = m_curVarsp->findIdName(nodep->name()); + AstCell* findcellp = findidp->castCell(); + if (!findidp) { + m_curVarsp->insert(nodep->name(), nodep); + } else if (!findcellp) { + nodep->v3error("Unsupported in C: Cell has same name as " + <prettyName()); + } else if (findcellp != nodep) { + nodep->v3error("Duplicate name of cell: "<prettyName()); + } + } + if (!nodep->modp()) { + nodep->v3fatalSrc("Cell has unlinked module"); // V3LinkCell should have errored out + } + else { + // Need to pass the module info to this cell, so we can link up the pin names + m_cellVarsp = nodep->modp()->userp()->castSymTable(); + UINFO(4,"(Backto) Link Cell: "<dumpTree(cout,"linkcell:"); } + //if (debug()) { nodep->modp()->dumpTree(cout,"linkcemd:"); } + nodep->iterateChildren(*this); + m_cellVarsp = NULL; + } + // Parent module inherits child's publicity + // This is done bottom up in the LinkBotupVisitor stage + } + + virtual void visit(AstPort* nodep, AstNUser*) { + // Port: Stash the pin number + if (!m_curVarsp) nodep->v3fatalSrc("Port not under module??\n"); + if (m_idState==ID_PARAM) { + // Need to set pin numbers after varnames are created + // But before we do the final resolution based on names + AstVar* refp = m_curVarsp->findIdName(nodep->name())->castVar(); + if (!refp) { + nodep->v3error("Input/output/inout declaration not found for port: "<prettyName()); + } else if (!refp->isIO()) { + nodep->v3error("Pin is not a in/out/inout: "<prettyName()); + } else { + m_curVarsp->insert("__pinNumber"+cvtToStr(nodep->pinNum()), refp); + } + // Ports not needed any more + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } + } + + void pinImplicitExprRecurse(AstNode* nodep) { + // Under a pin, Check interconnect expression for a pin reference or a concat. + // Create implicit variable as needed + if (AstVarRef* varrefp = nodep->castVarRef()) { + // To prevent user errors, we should only do single bit + // implicit vars, however some netlists (MIPS) expect single + // bit implicit wires to get created with range 0:0 etc. + //if (!nodep->modVarp()->rangep()) ... + createImplicitVar(varrefp, false); + } + else if (AstConcat* concp = nodep->castConcat()) { + pinImplicitExprRecurse(concp->lhsp()); + pinImplicitExprRecurse(concp->rhsp()); + } + } + + virtual void visit(AstPin* nodep, AstNUser*) { + // Pin: Link to submodule's pin + if (!m_cellVarsp) nodep->v3fatalSrc("Pin not under cell?\n"); + if (m_idState==ID_RESOLVE && !nodep->modVarp()) { + AstVar* refp = m_cellVarsp->findIdName(nodep->name())->castVar(); + if (!refp) { + nodep->v3error("Pin not found: "<prettyName()); + } else if (!refp->isIO() && !refp->isParam()) { + nodep->v3error("Pin is not a in/out/inout/param: "<prettyName()); + } else { + nodep->modVarp(refp); + } + } + // Deal with implicit definitions + if (m_idState==ID_RESOLVE && nodep->modVarp()) { + pinImplicitExprRecurse(nodep->exprp()); + } + nodep->iterateChildren(*this); + } + + virtual void visit(AstAssignW* nodep, AstNUser*) { + // Deal with implicit definitions + if (nodep->allowImplicit()) { + if (AstVarRef* forrefp = nodep->lhsp()->castVarRef()) { + createImplicitVar(forrefp, false); + } + } + nodep->iterateChildren(*this); + } + + virtual void visit(AstDefParam* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (m_idState==ID_PARAM) { + AstNode* findidp = m_curVarsp->findIdName(nodep->path()); + AstCell* cellp = findidp->castCell(); + if (!cellp) { + nodep->v3error("In defparam, cell "<path()<<" never declared"); + } else { + AstNode* exprp = nodep->rhsp()->unlinkFrBack(); + UINFO(9,"Defparam cell "<path()<<"."<name() + <<" <= "<fileline(), + -1, // Pin# not relevant + nodep->name(), + exprp); + cellp->addParamsp(pinp); + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } + } + } + + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + LinkVisitor(AstNetlist* rootp) { + m_curVarsp = NULL; + m_cellVarsp = NULL; + m_modp = NULL; + m_paramNum = 0; + // + rootp->accept(*this); + } + virtual ~LinkVisitor() { + for (vector::iterator it = m_delSymps.begin(); it != m_delSymps.end(); ++it) { + delete (*it); + } + } +}; + +//###################################################################### +// Link class functions + +void V3Link::link(AstNetlist* rootp) { + UINFO(4,__FUNCTION__<<": "< +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3LinkCells.h" +#include "V3SymTable.h" +#include "V3Read.h" +#include "V3Ast.h" +#include "V3Graph.h" + +//###################################################################### +// Graph subclasses + +class LinkCellsGraph : public V3Graph { +public: + LinkCellsGraph() {} + virtual ~LinkCellsGraph() {} + virtual void loopsMessageCb(V3GraphVertex* vertexp); +}; + + +class LinkCellsVertex : public V3GraphVertex { + AstModule* m_modp; +public: + LinkCellsVertex(V3Graph* graphp, AstModule* modp) + : V3GraphVertex(graphp), m_modp(modp) {} + virtual ~LinkCellsVertex() {} + AstModule* modp() const { return m_modp; } + virtual string name() const { return modp()->name(); } +}; + +class LibraryVertex : public V3GraphVertex { +public: + LibraryVertex(V3Graph* graphp) + : V3GraphVertex(graphp) {} + virtual ~LibraryVertex() {} + virtual string name() const { return "*LIBRARY*"; } +}; + +void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) { + if (LinkCellsVertex* vvertexp = dynamic_cast(vertexp)) { + vvertexp->modp()->v3error("Recursive module (module instantiates itself): " + <modp()->name()); + V3Error::abortIfErrors(); + } else { // Everything should match above, but... + v3fatalSrc("Recursive instantiations"); + } +} + +//###################################################################### +// Link state, as a visitor of each AstNode + +class LinkCellsVisitor : public AstNVisitor { +private: + // NODE STATE + // Entire netlist: + // AstModule::userp() // V3GraphVertex* Vertex describing this module + + // STATE + // Below state needs to be preserved between each module call. + AstModule* m_modp; // Current module + V3SymTable m_mods; // Symbol table of all module names + LinkCellsGraph m_graph; // Linked graph of all cell interconnects + LibraryVertex* m_libVertexp; // Vertex at root of all libraries + + //int debug() { return 9; } + + // METHODS + V3GraphVertex* vertex(AstModule* nodep) { + // Return corresponding vertex for this module + if (!nodep->userp()) { + nodep->userp(new LinkCellsVertex(&m_graph, nodep)); + } + return (nodep->userp()->castGraphVertex()); + } + + // VISITs + virtual void visit(AstNetlist* nodep, AstNUser*) { + AstNode::userClearTree(); + readModNames(); + nodep->iterateChildren(*this); + // Find levels in graph + m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); + m_graph.dumpDotFilePrefixed("linkcells"); + m_graph.rank(); + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (LinkCellsVertex* vvertexp = dynamic_cast(itp)) { + // +1 so we leave level 1 for the new wrapper we'll make in a moment + vvertexp->modp()->level(vvertexp->rank()+1); + } + } + } + virtual void visit(AstModule* nodep, AstNUser*) { + // Module: Pick up modnames, so we can resolve cells later + m_modp = nodep; + UINFO(2,"Link Module: "<name().find("__") != string::npos) { + nodep->v3error("Unsupported: Double underscores (__) in module names reserved for internal use: "<prettyName()); + } + if (nodep->inLibrary()) { + if (!m_libVertexp) m_libVertexp = new LibraryVertex(&m_graph); + new V3GraphEdge(&m_graph, m_libVertexp, vertex(nodep), 1, false); + } + nodep->iterateChildren(*this); + nodep->checkTree(); + m_modp = NULL; + } + + virtual void visit(AstCell* nodep, AstNUser*) { + // Cell: Resolve its filename. If necessary, parse it. + if (!nodep->modp()) { + UINFO(4,"Link Cell: "<modName())->castModule(); + if (!modp) { + // Read-subfile + V3Read reader (v3Global.rootp()); + reader.readFile(nodep->fileline(), nodep->modName(), false); + V3Error::abortIfErrors(); + // We've read new modules, grab new pointers to their names + readModNames(); + // Check again + modp = m_mods.findIdName(nodep->modName())->castModule(); + if (!modp) { + nodep->v3error("Can't resolve module reference: "<modName()); + } + } + if (modp) { + nodep->modp(modp); + // Track module depths, so can sort list from parent down to children + new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false); + } + } + // Convert unnamed pins to pin number based assignments + for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) { + if (pinp->name()=="") pinp->name("__pinNumber"+cvtToStr(pinp->pinNum())); + } + for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) { + if (pinp->name()=="") pinp->name("__paramNumber"+cvtToStr(pinp->pinNum())); + } + if (nodep->modp()) { + nodep->iterateChildren(*this); + } + } + + // Accelerate the recursion + // Must do statements to support Generates, math though... + virtual void visit(AstNodeMath* nodep, AstNUser*) {} + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + nodep->iterateChildren(*this); + } + + // METHODS + void readModNames() { + // Look at all modules, and store pointers to all module names + for (AstModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castModule()) { + if (!m_mods.findIdName(nodep->name())) { + m_mods.insert(nodep->name(), nodep); + } + } + } + +public: + // CONSTUCTORS + LinkCellsVisitor() { + m_modp = NULL; + m_libVertexp = NULL; + } + virtual ~LinkCellsVisitor() { + } + void main(AstNetlist* rootp) { + rootp->accept(*this); + } +}; + +//###################################################################### +// Link class functions + +void V3LinkCells::link(AstNetlist* rootp) { + UINFO(4,__FUNCTION__<<": "< +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3LinkDot.h" +#include "V3SymTable.h" +#include "V3Graph.h" +#include "V3Ast.h" + +//###################################################################### + +class LinkDotGraph : public V3Graph { +public: + LinkDotGraph() {} + virtual ~LinkDotGraph() {} + virtual string dotRankDir() { return "LR"; } +}; + +class LinkDotBaseVertex : public V3GraphVertex { + typedef std::map NameVtxMap; + // A point in the hierarchy, either inlined or real + string m_symPrefix; // String to prefix symbols with + NameVtxMap m_nameToVtxMap; // Lookup of name -> to vertexes +public: + LinkDotBaseVertex(V3Graph* graphp, const string& symPrefix) + : V3GraphVertex(graphp), m_symPrefix(symPrefix) {} + virtual ~LinkDotBaseVertex() {} + virtual string modName() const = 0; + virtual string cellName() const = 0; + virtual V3SymTable& syms() = 0; + string symPrefix() const { return m_symPrefix; } + void insertSubcellName(const string& name, LinkDotBaseVertex* toVertexp) { + m_nameToVtxMap.insert(make_pair(name,toVertexp)); + } + LinkDotBaseVertex* findSubcell(const string& name) { + // Find a vertex under this one by name. + // We could walk the edge top() list, but that would be O(n) for large lists of cells + NameVtxMap::iterator iter = m_nameToVtxMap.find(name); + if (iter == m_nameToVtxMap.end()) return NULL; + else return iter->second; + } +}; + +class LinkDotCellVertex : public LinkDotBaseVertex { + // A real point in the hierarchy, corresponding to a instantiated module + AstModule* m_modp; // Module + AstCell* m_cellp; // Cell creating this vertex **NULL AT TOP** + V3SymTable m_syms; // Symbol table of variable/task names for global lookup +public: + LinkDotCellVertex(V3Graph* graphp, AstCell* nodep) + : LinkDotBaseVertex(graphp, ""), m_modp(nodep->modp()), m_cellp(nodep) {} + LinkDotCellVertex(V3Graph* graphp, AstModule* nodep) + : LinkDotBaseVertex(graphp, ""), m_modp(nodep), m_cellp(NULL) {} + virtual ~LinkDotCellVertex() {} + AstModule* modp() const { return m_modp; } // May be NULL + AstCell* cellp() const { return m_cellp; } // Is NULL at TOP + virtual V3SymTable& syms() { return m_syms; } + // We need to use origName as parameters may have renamed the modname + virtual string modName() const { return (modp() ? modp()->origName() : "*NULL*"); } + virtual string cellName() const { return (cellp() ? cellp()->origName() : "*NULL*"); } + virtual string name() const { return (string)("C:")+cellName()+" M:"+modName(); } +}; + +class LinkDotInlineVertex : public LinkDotBaseVertex { + // A fake point in the hierarchy, corresponding to a inlined module + // This refrences to another vertex, and eventually resolves to a module with a prefix + string m_basename; // Name with dotteds stripped + AstCellInline* m_cellInlinep; // Inlined cell + LinkDotCellVertex* m_symVxp; // Above cell so we can find real symbol table + // // (Could walk graph to find it, but that's much slower.) +public: + LinkDotInlineVertex(V3Graph* graphp, AstCellInline* nodep, LinkDotCellVertex* symVxp, + const string& basename) + : LinkDotBaseVertex(graphp, nodep->name()+"__DOT__") + , m_basename(basename), m_cellInlinep(nodep), m_symVxp(symVxp) {} + virtual ~LinkDotInlineVertex() {} + AstCellInline* cellInlinep() const { return m_cellInlinep; } + // Search up through tree to find the real symbol table. + virtual V3SymTable& syms() { return m_symVxp->syms(); } + virtual string modName() const { return cellInlinep()->origModName(); } + virtual string cellName() const { return m_basename; } + virtual string name() const { return (string)("INL C:")+cellName()+" M:"+modName()+" P:"+symPrefix(); } + virtual string dotColor() const { return "yellow"; } +}; + +//###################################################################### +// LinkDot state, as a visitor of each AstNode + +class LinkDotState { +private: + // NODE STATE + // Cleared on Netlist + // AstModule::userp() -> LinkDotCellVertex*. Last cell that uses this module + // AstVarScope::user2p() -> AstVarScope*. Base alias for this signal + + // TYPES + typedef std::multimap NameScopeMap; + // MEMBERS + LinkDotGraph m_graph; // Graph of hiearchy + NameScopeMap m_nameScopeMap; // Hash of scope referenced by textual name + bool m_forScopeCreation; // Remove VarXRefs for V3Scope +public: + static int debug() { return V3Error::debugDefault(); } +// static int debug() { return 9; } + + // CONSTRUCTORS + LinkDotState(bool forScopeCreation) { + UINFO(4,__FUNCTION__<<": "<userp(vxp); + if (forScopeCreation()) m_nameScopeMap.insert(make_pair(scopename, vxp)); + return vxp; + } + LinkDotCellVertex* insertCell(LinkDotBaseVertex* abovep, LinkDotCellVertex* cellVxp, + AstCell* nodep, const string& scopename) { + UINFO(9," INSERTcell "<modp()) nodep->modp()->userp(vxp); + new V3GraphEdge(&m_graph, abovep, vxp, 1, false); + abovep->insertSubcellName(nodep->origName(), vxp); + if (abovep != cellVxp) { + // If it's foo_DOT_bar, we need to be able to find it under that too. + cellVxp->insertSubcellName(nodep->name(), vxp); + } + if (forScopeCreation()) m_nameScopeMap.insert(make_pair(scopename, vxp)); + return vxp; + } + LinkDotInlineVertex* insertInline(LinkDotBaseVertex* abovep, LinkDotCellVertex* cellVxp, + AstCellInline* nodep, const string& basename) { + UINFO(9," INSERTcinl "<insertSubcellName(basename, vxp); + if (abovep != cellVxp) { + // If it's foo_DOT_bar, we need to be able to find it under that too. + cellVxp->insertSubcellName(nodep->name(), vxp); + } + return vxp; + } + void insertSym(LinkDotCellVertex* abovep, const string& name, AstNode* nodep) { + UINFO(9," INSERTsym "<syms().insert(name, nodep); + } + bool existsModScope(AstModule* nodep) { + return nodep->userp()!=NULL; + } + LinkDotCellVertex* findModScope(AstModule* nodep) { + LinkDotCellVertex* vxp = (LinkDotCellVertex*)(nodep->userp()); + if (!vxp) nodep->v3fatalSrc("Module never assigned a vertex"); + return vxp; + } + LinkDotCellVertex* findScope(AstScope* nodep) { + NameScopeMap::iterator iter = m_nameScopeMap.find(nodep->name()); + if (iter == m_nameScopeMap.end()) { + nodep->v3fatalSrc("Scope never assigned a vertex"); + } + return iter->second; + } + void dump() { + if (debug()>=6) m_graph.dumpDotFilePrefixed("linkdot"); + } +private: + LinkDotBaseVertex* parentOfCell(LinkDotBaseVertex* lowerVxp) { + for (V3GraphEdge* edgep = lowerVxp->inBeginp(); edgep; edgep=edgep->inNextp()) { + LinkDotBaseVertex* fromVxp = dynamic_cast(edgep->fromp()); + return fromVxp; + } + return NULL; + } +public: + LinkDotBaseVertex* findDotted(LinkDotBaseVertex* cellVxp, const string& dotname, string& baddot) { + // Given a dotted hiearchy name, return where in scope it is + // Note when dotname=="" we just fall through and return cellVxp + UINFO(8," dottedFind "<findSubcell(ident)) { + cellVxp = findVxp; + } + // Check this module - cur modname + else if (cellVxp->modName() == ident) {} + // Check this module - cur cellname + else if (cellVxp->cellName() == ident) {} + // Move up and check cellname + modname + else { + while (cellVxp) { + cellVxp = parentOfCell(cellVxp); + if (cellVxp) { + UINFO(9,"\t\tUp to "<modName() == ident + || cellVxp->cellName() == ident) { + break; + } + else if (LinkDotBaseVertex* findVxp = cellVxp->findSubcell(ident)) { + cellVxp = findVxp; + break; + } + } + } + if (!cellVxp) return NULL; // Not found + } + } else { // Searching for middle submodule, must be a cell name + if (LinkDotBaseVertex* findVxp = cellVxp->findSubcell(ident)) { + cellVxp = findVxp; + } else { + return NULL; // Not found + } + } + firstId = false; + } + return cellVxp; + } + + AstNode* findSym(LinkDotBaseVertex* cellVxp, const string& dotname, string& baddot) { + // Find symbol in given point in hierarchy + // For simplicity cellVxp may be passed NULL result from findDotted + if (!cellVxp) return NULL; + UINFO(8,"\t\tfindSym "<symPrefix()=="") ? "" : " as ") + <<((cellVxp->symPrefix()=="") ? "" : cellVxp->symPrefix()+dotname) + <<" at "<syms().findIdName(cellVxp->symPrefix() + dotname); // Might be NULL + if (!nodep) baddot = dotname; + return nodep; + } +}; + +//====================================================================== + +class LinkDotFindVisitor : public AstNVisitor { +private: + // STATE + LinkDotState* m_statep; // State to pass between visitors, including symbol table + LinkDotCellVertex* m_cellVxp; // Vertex for current module + LinkDotBaseVertex* m_inlineVxp; // Vertex for current module, possibly a fake inlined one + string m_scope; // Scope text + AstBegin* m_beginp; // Current Begin/end block + int debug() { return LinkDotState::debug(); } + + // VISITs + virtual void visit(AstNetlist* nodep, AstNUser*) { + // The first module in the list is always the top module (sorted before this is called). + // This may not be the module with isTop() set, as early in the steps, + // wrapTop may have not been created yet. + AstModule* topmodp = nodep->modulesp(); + if (!topmodp) nodep->v3fatalSrc("No top level module"); + UINFO(8,"Top Module: "<insertTopCell(topmodp, m_scope); + m_inlineVxp = m_cellVxp; + { + topmodp->accept(*this); + } + m_scope = ""; + m_cellVxp = NULL; + m_inlineVxp = m_cellVxp; + } + virtual void visit(AstModule* nodep, AstNUser*) { + UINFO(8," "<iterateChildren(*this); + } + } + virtual void visit(AstScope* nodep, AstNUser*) { + if (!m_statep->forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope"); + // Ignored. Processed in next step + } + virtual void visit(AstCell* nodep, AstNUser*) { + UINFO(5," CELL under "<prettyName(); + string::size_type pos; + if ((pos = origname.rfind(".")) != string::npos) { + // Flattened, find what CellInline it should live under + string scope = origname.substr(0,pos); + string baddot; + aboveVxp = m_statep->findDotted(aboveVxp, scope, baddot); + if (!aboveVxp) nodep->v3fatalSrc("Can't find cell insertion point at '"<prettyName()); + } + { + m_scope = m_scope+"."+nodep->name(); + m_cellVxp = m_statep->insertCell(aboveVxp, m_cellVxp, nodep, m_scope); + m_inlineVxp = m_cellVxp; + if (nodep->modp()) nodep->modp()->accept(*this); + } + m_scope = oldscope; + m_cellVxp = oldVxp; + m_inlineVxp = m_cellVxp; + } + virtual void visit(AstCellInline* nodep, AstNUser*) { + UINFO(5," CELLINLINE under "<name(); + string::size_type pos; + if ((pos=dottedname.rfind("__DOT__")) != string::npos) { + string dotted = dottedname.substr(0, pos); + string ident = dottedname.substr(pos+strlen("__DOT__")); + string baddot; + aboveVxp = m_statep->findDotted(aboveVxp, dotted, baddot); + if (!aboveVxp) nodep->v3fatalSrc("Can't find cellinline insertion point at '"<prettyName()); + m_statep->insertInline(aboveVxp, m_cellVxp, nodep, ident); + } else { // No __DOT__, just directly underneath + m_statep->insertInline(aboveVxp, m_cellVxp, nodep, nodep->name()); + } + } + virtual void visit(AstBegin* nodep, AstNUser*) { + UINFO(5," "<iterateChildren(*this); + m_beginp = oldbegin; + } + virtual void visit(AstVar* nodep, AstNUser*) { + if (!m_statep->forScopeCreation() + && !m_beginp // For now, we don't support xrefs into begin blocks + && !nodep->isFuncLocal()) { + m_statep->insertSym(m_cellVxp, nodep->name(), nodep); + } + } + virtual void visit(AstNodeFTask* nodep, AstNUser*) { + m_statep->insertSym(m_cellVxp, nodep->name(), nodep); + // No recursion, we don't want to pick up variables + } + virtual void visit(AstCFunc* nodep, AstNUser*) { + // Ignore all AstVars under functions + } + + // For speed, don't recurse things that can't have cells + virtual void visit(AstNodeStmt*, AstNUser*) {} + virtual void visit(AstNodeMath*, AstNUser*) {} + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + LinkDotFindVisitor(AstNetlist* rootp, LinkDotState* statep) { + UINFO(4,__FUNCTION__<<": "<accept(*this); + } + virtual ~LinkDotFindVisitor() {} +}; + +//====================================================================== + +class LinkDotScopeVisitor : public AstNVisitor { +private: + // STATE + LinkDotState* m_statep; // State to pass between visitors, including symbol table + LinkDotCellVertex* m_cellVxp; // Vertex for current module + int debug() { return LinkDotState::debug(); } + + // VISITs + virtual void visit(AstScope* nodep, AstNUser*) { + UINFO(8," SCOPE "<forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope"); + // Using the CELL names, we created all hiearchy. We now need to match this Scope + // up with the hiearchy created by the CELL names. + m_cellVxp = m_statep->findScope(nodep); + nodep->iterateChildren(*this); + m_cellVxp = NULL; + } + virtual void visit(AstVarScope* nodep, AstNUser*) { + if (!nodep->varp()->isFuncLocal()) { + m_statep->insertSym(m_cellVxp, nodep->varp()->name(), nodep); + } + } + virtual void visit(AstNodeFTask* nodep, AstNUser*) { + m_statep->insertSym(m_cellVxp, nodep->name(), nodep); + // No recursion, we don't want to pick up variables + } + virtual void visit(AstAssignAlias* nodep, AstNUser*) { + // Track aliases created by V3Inline; if we get a VARXREF(aliased_from) + // we'll need to replace it with a VARXREF(aliased_to) + if (m_statep->forScopeCreation()) { + if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\talias: "); + AstVarScope* fromVscp = nodep->lhsp()->castVarRef()->varScopep(); + AstVarScope* toVscp = nodep->rhsp()->castVarRef()->varScopep(); + if (!fromVscp || !toVscp) nodep->v3fatalSrc("Bad alias scopes"); + fromVscp->user2p(toVscp); + } + nodep->iterateChildren(*this); + } + // For speed, don't recurse things that can't have scope + virtual void visit(AstCell*, AstNUser*) {} + virtual void visit(AstVar*, AstNUser*) {} + virtual void visit(AstNodeStmt*, AstNUser*) {} + virtual void visit(AstNodeMath*, AstNUser*) {} + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + LinkDotScopeVisitor(AstNetlist* rootp, LinkDotState* statep) { + UINFO(4,__FUNCTION__<<": "<accept(*this); + } + virtual ~LinkDotScopeVisitor() {} +}; + +//====================================================================== + +class LinkDotResolveVisitor : public AstNVisitor { +private: + // STATE + LinkDotState* m_statep; // State, including dotted symbol table + LinkDotCellVertex* m_cellVxp; // Vertex for current module + int debug() { return LinkDotState::debug(); } + + // METHODS + + // VISITs + virtual void visit(AstModule* nodep, AstNUser*) { + UINFO(8," "<existsModScope(nodep)) { + UINFO(5,"Dead module for "<findModScope(nodep); + } + nodep->iterateChildren(*this); + m_cellVxp = NULL; + } + virtual void visit(AstScope* nodep, AstNUser*) { + UINFO(8," "<findScope(nodep); + nodep->iterateChildren(*this); + m_cellVxp = NULL; + } + virtual void visit(AstCellInline* nodep, AstNUser*) { + if (m_statep->forScopeCreation()) { + nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL; + } + } + virtual void visit(AstVarXRef* nodep, AstNUser*) { + // VarRef: Resolve its reference + // We always link even if varp() is set, because the module we choose may change + // due to creating new modules, flattening, etc. + UINFO(8," "<varp(NULL); // Module that is not in hiearchy. We'll be dead code eliminating it later. + } else { + string baddot; + LinkDotBaseVertex* dotVxp = m_cellVxp; // Start search at current scope + if (nodep->inlinedDots()!="") { // Correct for current scope + dotVxp = m_statep->findDotted(dotVxp, nodep->inlinedDots(), baddot); + if (!dotVxp) nodep->v3fatalSrc("Couldn't resolve inlined scope '"<inlinedDots()); + } + dotVxp = m_statep->findDotted(dotVxp, nodep->dotted(), baddot); // Maybe NULL + if (!m_statep->forScopeCreation()) { + AstVar* varp = (m_statep->findSym(dotVxp, nodep->name(), baddot) + ->castVar()); // maybe NULL + nodep->varp(varp); + UINFO(7," Resolved "<varp()) { + nodep->v3error("Can't find definition of '"<dotted()+"."+nodep->prettyName()); + } + } else { + string baddot; + AstVarScope* vscp = (m_statep->findSym(dotVxp, nodep->name(), baddot) + ->castVarScope()); // maybe NULL + if (!vscp) { + nodep->v3error("Can't find varpin scope of '"<dotted()+"."+nodep->prettyName()); + } else { + while (vscp->user2p()) { // If V3Inline aliased it, pick up the new signal + UINFO(7," Resolved pre-alias "<user2p()->castNode()->castVarScope(); + } + // Convert the VarXRef to a VarRef, so we don't need later optimizations to deal with VarXRef. + nodep->varp(vscp->varp()); + nodep->varScopep(vscp); + UINFO(7," Resolved "<fileline(), vscp, nodep->lvalue()); + nodep->replaceWith(newvscp); + nodep->deleteTree(); nodep=NULL; + } + } + } + } + virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { + UINFO(8," "<taskp(NULL); // Module that is not in hiearchy. We'll be dead code eliminating it later. + } else { + string baddot; + LinkDotBaseVertex* dotVxp = m_cellVxp; // Start search at current scope + if (nodep->inlinedDots()!="") { // Correct for current scope + dotVxp = m_statep->findDotted(dotVxp, nodep->inlinedDots(), baddot); + if (!dotVxp) nodep->v3fatalSrc("Couldn't resolve inlined scope '"<inlinedDots()); + } + dotVxp = m_statep->findDotted(dotVxp, nodep->dotted(), baddot); // Maybe NULL + + AstNodeFTask* taskp = (m_statep->findSym(dotVxp, nodep->name(), baddot) + ->castNodeFTask()); // maybe NULL + nodep->taskp(taskp); + UINFO(7," Resolved "<taskp()) { + nodep->v3error("Can't find definition of '"<dotted()+"."+nodep->prettyName()); + } + } + nodep->iterateChildren(*this); + } + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + LinkDotResolveVisitor(AstNetlist* rootp, LinkDotState* statep) { + UINFO(4,__FUNCTION__<<": "<accept(*this); + } + virtual ~LinkDotResolveVisitor() {} +}; + +//###################################################################### +// Link class functions + +void V3LinkDot::linkDot(AstNetlist* rootp) { + UINFO(2,__FUNCTION__<<": "<=5) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot.tree")); + LinkDotState state (false); + LinkDotFindVisitor visitor(rootp,&state); + state.dump(); + LinkDotResolveVisitor visitorb(rootp,&state); +} + +void V3LinkDot::linkDotScope(AstNetlist* rootp) { + UINFO(2,__FUNCTION__<<": "<=5) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot.tree")); + LinkDotState state (true); + LinkDotFindVisitor visitor(rootp,&state); + // Process AstScope's. This needs to be separate pass after whole hiearchy graph created. + LinkDotScopeVisitor visitors(rootp,&state); + state.dump(); + LinkDotResolveVisitor visitorb(rootp,&state); +} diff --git a/src/V3LinkDot.h b/src/V3LinkDot.h new file mode 100644 index 000000000..ca6e9b173 --- /dev/null +++ b/src/V3LinkDot.h @@ -0,0 +1,36 @@ +// $Id$ //-*- C++ -*- +//************************************************************************* +// DESCRIPTION: Verilator: Link XREF signals/functions 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 _V3LINKDOT_H_ +#define _V3LINKDOT_H_ 1 +#include "config.h" +#include "V3Error.h" +#include "V3Ast.h" + +//============================================================================ + +class V3LinkDot { +public: + static void linkDot(AstNetlist* nodep); + static void linkDotScope(AstNetlist* nodep); +}; + +#endif // Guard diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp new file mode 100644 index 000000000..3e884d90d --- /dev/null +++ b/src/V3LinkLevel.cpp @@ -0,0 +1,159 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Resolve module/signal name 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. +// +//************************************************************************* +// LINKTOP TRANSFORMATIONS: +// Utility functions +// Sort cells by depth +// Create new MODULE TOP with connections to below signals +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3LinkLevel.h" +#include "V3Ast.h" + +//###################################################################### +// Levelizing class functions + +class LinkLevelVisitor : public AstNVisitor { +private: + // STATE + AstModule* m_modp; + // VISITs + virtual void visit(AstModule* nodep, AstNUser*) { + m_modp = nodep; + nodep->iterateChildren(*this); + } + virtual void visit(AstCell* nodep, AstNUser*) { + // Track module depths, so can sort list from parent down to children + nodep->modp()->level(max(nodep->modp()->level(), (1+m_modp->level()))); + UINFO(5," Under "<modp()<modp()->level()>99) nodep->v3error("Over 99 levels of cell hierarchy. Probably cell instantiates itself."); + // Recurse in, preserving state + AstModule* holdmodp = m_modp; + nodep->modp()->accept(*this); + m_modp = holdmodp; + } + // For speed, don't recurse things that can't have cells + virtual void visit(AstNodeStmt*, AstNUser*) {} + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + LinkLevelVisitor() {} + virtual ~LinkLevelVisitor() {} + void main(AstNetlist* rootp) { + rootp->accept(*this); + } +}; + +struct CmpLevel { + inline bool operator () (const AstModule* lhsp, const AstModule* rhsp) const { + return lhsp->level() < rhsp->level(); + } +}; + +void V3LinkLevel::modSortByLevel() { + // Sort modules by levels, root down to lowest children + // Calculate levels again in case we added modules + UINFO(2,"modSortByLevel()\n"); + LinkLevelVisitor visitor; + visitor.main(v3Global.rootp()); + + vector vec; + AstModule* topp = NULL; + for (AstModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castModule()) { + if (nodep->level()<=2) { + if (topp) nodep->v3error("Unsupported: Multiple top level modules: " + <prettyName()<<" and "<prettyName()); + topp = nodep; + } + vec.push_back(nodep); + } + sort(vec.begin(), vec.end(), CmpLevel()); // Sort the vector + for (vector::iterator it = vec.begin(); it != vec.end(); ++it) { + AstModule* nodep = *it; + nodep->unlinkFrBack(); + } + if (v3Global.rootp()->modulesp()) v3Global.rootp()->v3fatalSrc("Unlink didn't work"); + for (vector::iterator it = vec.begin(); it != vec.end(); ++it) { + AstModule* nodep = *it; + v3Global.rootp()->addModulep(nodep); + } +} + +//###################################################################### +// Wrapping + +void V3LinkLevel::wrapTop(AstNetlist* netlistp) { + UINFO(2,__FUNCTION__<<": "<modulesp(); + AstModule* newmodp = new AstModule(oldmodp->fileline(), (string)"TOP_"+oldmodp->name()); + // Make the new module first in the list + oldmodp->unlinkFrBackWithNext(); + newmodp->addNext(oldmodp); + newmodp->level(1); + newmodp->modPublic(true); + netlistp->addModulep(newmodp); + + // Add instance + AstCell* cellp = new AstCell(newmodp->fileline(), + (v3Global.opt.l2Name() ? "v" : oldmodp->name()), + oldmodp->name(), + NULL, NULL, NULL); + cellp->modp(oldmodp); + newmodp->addStmtp(cellp); + + // Add pins + for (AstNode* subnodep=oldmodp->stmtsp(); subnodep; subnodep = subnodep->nextp()) { + if (AstVar* oldvarp=subnodep->castVar()) { + UINFO(8,"VARWRAP "<isIO()) { + AstVar* varp = oldvarp->cloneTree(false)->castVar(); + newmodp->addStmtp(varp); + varp->sigPublic(true); // User needs to be able to get to it... + if (oldvarp->isInput() || oldvarp->isOutput()) { + oldvarp->primaryIO(true); + varp->primaryIO(true); + } + if (varp->isIO() && v3Global.opt.systemC()) varp->sc(true); + + AstPin* pinp = new AstPin(oldvarp->fileline(),0,oldvarp->name(), + new AstVarRef(varp->fileline(), + varp, oldvarp->isOutput())); + // Skip length and width comp; we know it's a direct assignment + pinp->modVarp(oldvarp); + pinp->widthSignedFrom(oldvarp); + cellp->addPinsp(pinp); + } + } + } +} diff --git a/src/V3LinkLevel.h b/src/V3LinkLevel.h new file mode 100644 index 000000000..bc7f3ab89 --- /dev/null +++ b/src/V3LinkLevel.h @@ -0,0 +1,36 @@ +// $Id$ //-*- C++ -*- +//************************************************************************* +// DESCRIPTION: Verilator: Link 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 _V3LINKLEVEL_H_ +#define _V3LINKLEVEL_H_ 1 +#include "config.h" +#include "V3Error.h" +#include "V3Ast.h" + +//============================================================================ + +class V3LinkLevel { +public: + static void modSortByLevel(); + static void wrapTop(AstNetlist* nodep); +}; + +#endif // Guard diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp new file mode 100644 index 000000000..f108f2e87 --- /dev/null +++ b/src/V3LinkResolve.cpp @@ -0,0 +1,444 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Resolve module/signal name 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. +// +//************************************************************************* +// LinkResolve TRANSFORMATIONS: +// Top-down traversal +// Extracts: +// Add SUB so that we subtract off the "base 0-start" of the array +// SelBit: Convert to ArraySel +// Add SUB so that we subtract off the "base 0-start" of the array +// File operations +// Convert normal var to FILE* type +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3LinkResolve.h" +#include "V3Ast.h" + +//###################################################################### +// Link state, as a visitor of each AstNode + +class LinkResolveVisitor : public AstNVisitor { +private: + // NODE STATE + // Entire netlist: + // AstCaseItem::user2() // bool Moved default caseitems + + // STATE + // Below state needs to be preserved between each module call. + AstModule* m_modp; // Current module + AstNodeFTask* m_ftaskp; // Function or task we're inside + bool m_setRefLvalue; // Set VarRefs to lvalues for pin assignments + AstBegin* m_beginLastp; // Last begin block seen (not present one) + int m_beginNum; // Begin block number, 0=none seen + + //int debug() { return 9; } + + // METHODS + // VISITs + virtual void visit(AstNetlist* nodep, AstNUser*) { + AstNode::user2ClearTree(); + // And recurse... + nodep->iterateChildren(*this); + } + + virtual void visit(AstModule* nodep, AstNUser*) { + // Module: Create sim table for entire module and iterate + UINFO(8,"MODULE "<iterateChildren(*this); + m_modp = NULL; + } + virtual void visit(AstBegin* nodep, AstNUser*) { + // Rename "genblk"s to include a number if there was more then one per block + // This is better then doing it at parse time, because if there's only one, it's named logically. + UINFO(8," "<v3fatalSrc("Where was first begin?"); + m_beginLastp->name(m_beginLastp->name()+"1"); + } + if (m_beginNum > 1) { // Now us + nodep->name(nodep->name()+cvtToStr(m_beginNum)); + } + // Recurse + int oldNum = m_beginNum; + m_beginNum = 0; + m_beginLastp = NULL; + { + nodep->iterateChildren(*this); + } + m_beginNum = oldNum; + m_beginLastp = nodep; // Us, so 2nd begin can change our name + } + virtual void visit(AstVar* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (nodep->arraysp() && nodep->isIO()) { + nodep->v3error("Arrayed variables may not be inputs nor outputs"); + } + if (m_ftaskp) nodep->funcLocal(true); + if (nodep && nodep->isSigPublic()) m_modp->modPublic(true); // Avoid flattening if signals are exposed + } + + virtual void visit(AstNodeVarRef* nodep, AstNUser*) { + // VarRef: Resolve its reference + if (nodep->varp()) { + nodep->varp()->usedParam(true); + if (nodep->lvalue() && nodep->varp()->isInput()) { + nodep->v3error("Assigning to input variable: "<prettyName()); + } + } + if (m_setRefLvalue) { + nodep->lvalue(true); + } + nodep->iterateChildren(*this); + } + + virtual void visit(AstNodeFTask* nodep, AstNUser*) { + // NodeTask: Remember its name for later resolution + // Remember the existing symbol table scope + m_ftaskp = nodep; + nodep->iterateChildren(*this); + m_ftaskp = NULL; + } + + virtual void visit(AstPin* nodep, AstNUser*) { + if (nodep->modVarp() && nodep->modVarp()->isOutput()) { + // When the varref's were created, we didn't know the I/O state + // Now that we do, and it's from a output, we know it's a lvalue + m_setRefLvalue = true; + nodep->iterateChildren(*this); + m_setRefLvalue = false; + } else { + nodep->iterateChildren(*this); + } + } + + virtual void visit(AstSel* nodep, AstNUser*) { + nodep->lhsp()->iterateAndNext(*this); + { // Only set lvalues on the from + bool last_setRefLvalue = m_setRefLvalue; + m_setRefLvalue = false; + nodep->rhsp()->iterateAndNext(*this); + nodep->thsp()->iterateAndNext(*this); + m_setRefLvalue = last_setRefLvalue; + } + } + virtual void visit(AstArraySel* nodep, AstNUser*) { + nodep->lhsp()->iterateAndNext(*this); + { // Only set lvalues on the from + bool last_setRefLvalue = m_setRefLvalue; + m_setRefLvalue = false; + nodep->rhsp()->iterateAndNext(*this); + m_setRefLvalue = last_setRefLvalue; + } + } + void iterateSelTriop(AstNodePreSel* nodep) { + nodep->lhsp()->iterateAndNext(*this); + { // Only set lvalues on the from + bool last_setRefLvalue = m_setRefLvalue; + m_setRefLvalue = false; + nodep->rhsp()->iterateAndNext(*this); + nodep->thsp()->iterateAndNext(*this); + m_setRefLvalue = last_setRefLvalue; + } + } + + AstNode* newSubAttrOf(AstNode* underp, AstNode* fromp, AstAttrType attrType) { + // Account for a variable's LSB in bit selections + // Replace underp with a SUB(underp, ATTROF(varp, attrType)) + int dimension=0; + while (fromp && !fromp->castNodeVarRef() && (fromp->castSel() || fromp->castArraySel())) { + if (fromp->castSel()) fromp = fromp->castSel()->fromp(); + else fromp = fromp->castArraySel()->fromp(); + dimension++; + } + AstNodeVarRef* varrefp = fromp->castNodeVarRef(); + if (!varrefp) fromp->v3fatalSrc("Bit/array selection of non-variable"); + if (!varrefp->varp()) varrefp->v3fatalSrc("Signal not linked"); + AstRange* vararrayp = varrefp->varp()->arrayp(dimension); + AstRange* varrangep = varrefp->varp()->rangep(); + if ((attrType==AstAttrType::ARRAY_LSB + // SUB #'s Not needed because LSB==0? (1D only, else we'll constify it later) + ? (vararrayp && !vararrayp->lsbp()->isZero()) + : (varrangep && !varrangep->lsbp()->isZero()))) { + AstNode* newrefp; + if (varrefp->castVarXRef()) { + newrefp = new AstVarXRef(underp->fileline(), + varrefp->varp(), varrefp->castVarXRef()->dotted(), false); + } else { + newrefp = new AstVarRef (underp->fileline(), + varrefp->varp(), false); + } + AstNode* newp = new AstSub (underp->fileline(), + underp, + new AstAttrOf(underp->fileline(), + attrType, newrefp, dimension)); + return newp; + } else { + return underp; + } + } + + void selCheckDimension(AstSel* nodep) { + // Perform error checks on the node + AstNode* fromp = nodep->lhsp(); + AstNode* basefromp = AstArraySel::baseFromp(fromp); + AstNodeVarRef* varrefp = basefromp->castNodeVarRef(); + AstVar* varp = varrefp ? varrefp->varp() : NULL; + if (fromp->castSel() + || (varp && !varp->rangep() && !varp->isParam())) { + nodep->v3error("Illegal bit select; variable already selected, or bad dimension"); + } + } + + virtual void visit(AstSelBit* nodep, AstNUser*) { + // Couldn't tell until link time if [#] references a bit or an array + iterateSelTriop(nodep); + AstNode* fromp = nodep->lhsp()->unlinkFrBack(); + AstNode* bitp = nodep->rhsp()->unlinkFrBack(); + AstNode* basefromp = AstArraySel::baseFromp(fromp); + int dimension = AstArraySel::dimension(fromp); + AstNodeVarRef* varrefp = basefromp->castNodeVarRef(); + if (varrefp + && varrefp->varp() + && varrefp->varp()->arrayp(dimension)) { + // SELBIT(array, index) -> ARRAYSEL(array, index) + AstNode* newp = new AstArraySel + (nodep->fileline(), + fromp, + newSubAttrOf(bitp, fromp, AstAttrType::ARRAY_LSB)); + nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; + } + else { + // SELBIT(range, index) -> SEL(array, index, 1) + V3Number one (nodep->fileline(),32,1); one.width(32,false); // Unsized so width from user + AstSel* newp = new AstSel + (nodep->fileline(), + fromp, + newSubAttrOf(bitp, fromp, AstAttrType::RANGE_LSB), + new AstConst (nodep->fileline(),one)); + selCheckDimension(newp); + nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; + } + } + + virtual void visit(AstSelExtract* nodep, AstNUser*) { + // SELEXTRACT(from,msb,lsb) -> SEL(from, lsb, 1+msb-lsb) + iterateSelTriop(nodep); + AstNode* fromp = nodep->lhsp()->unlinkFrBack(); + AstNode* msbp = nodep->rhsp()->unlinkFrBack(); + AstNode* lsbp = nodep->thsp()->unlinkFrBack(); + AstNode* widthp; + if (msbp->castConst() && lsbp->castConst()) { + // Quite common, save V3Const some effort + V3Number widnum (msbp->fileline(),32,msbp->castConst()->asInt() +1-lsbp->castConst()->asInt()); + widnum.width(32,false); // Unsized so width from user + widthp = new AstConst (msbp->fileline(), widnum); + pushDeletep(msbp); + } else { + V3Number one (nodep->fileline(),32,1); one.width(32,false); // Unsized so width from user + widthp = new AstSub (lsbp->fileline(), + new AstAdd(msbp->fileline(), + new AstConst(msbp->fileline(),one), + msbp), + lsbp->cloneTree(true)); + } + AstSel* newp = new AstSel + (nodep->fileline(), + fromp, + newSubAttrOf(lsbp, fromp, AstAttrType::RANGE_LSB), + widthp); + selCheckDimension(newp); + nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; + } + virtual void visit(AstSelPlus* nodep, AstNUser*) { + // SELPLUS -> SEL + iterateSelTriop(nodep); + AstNode* fromp = nodep->lhsp()->unlinkFrBack(); + AstNode* lsbp = nodep->rhsp()->unlinkFrBack(); + AstNode* widthp = nodep->thsp()->unlinkFrBack(); + AstSel* newp = new AstSel + (nodep->fileline(), + fromp, + newSubAttrOf(lsbp, fromp, AstAttrType::RANGE_LSB), + widthp); + selCheckDimension(newp); + nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; + } + virtual void visit(AstSelMinus* nodep, AstNUser*) { + // SELMINUS(from,msb,width) -> SEL(from, msb-(width-1)-lsb#) + iterateSelTriop(nodep); + AstNode* fromp = nodep->lhsp()->unlinkFrBack(); + AstNode* msbp = nodep->rhsp()->unlinkFrBack(); + AstNode* widthp = nodep->thsp()->unlinkFrBack(); + V3Number one (msbp->fileline(),32,1); one.width(32,false); // Unsized so width from user + AstSel* newp = new AstSel + (nodep->fileline(), + fromp, + newSubAttrOf(new AstSub (msbp->fileline(), + msbp, + new AstSub (msbp->fileline(), + widthp->cloneTree(true), + new AstConst (msbp->fileline(), one))), + fromp, AstAttrType::RANGE_LSB), + widthp); + selCheckDimension(newp); + nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; + } + + virtual void visit(AstCaseItem* nodep, AstNUser*) { + // Move default caseItems to the bottom of the list + // That saves us from having to search each case list twice, for non-defaults and defaults + nodep->iterateChildren(*this); + if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) { + nodep->user2(true); + AstNode* nextp = nodep->nextp(); + nodep->unlinkFrBack(); + nextp->addNext(nodep); + } + } + + virtual void visit(AstPragma* nodep, AstNUser*) { + if (nodep->pragType() == AstPragmaType::PUBLIC_MODULE) { + if (!m_modp) nodep->v3fatalSrc("PUBLIC_MODULE not under a module\n"); + m_modp->modPublic(true); + nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL; + } + else if (nodep->pragType() == AstPragmaType::PUBLIC_TASK) { + if (!m_ftaskp) nodep->v3fatalSrc("PUBLIC_TASK not under a task\n"); + m_ftaskp->taskPublic(true); + m_modp->modPublic(true); // Need to get to the task... + nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL; + } + else if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) { + if (!v3Global.opt.coverageLine()) { // No need for block statements; may optimize better without + nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL; + } + } + else { + nodep->iterateChildren(*this); + } + } + + void expectDescriptor(AstNode* nodep, AstNodeVarRef* filep) { + if (!filep) nodep->v3error("Unsupported: $fopen/$fclose descriptor must be a simple variable"); + if (filep && filep->varp()) filep->varp()->attrFileDescr(true); + } + virtual void visit(AstFOpen* nodep, AstNUser*) { + nodep->iterateChildren(*this); + expectDescriptor(nodep, nodep->filep()); + } + virtual void visit(AstFClose* nodep, AstNUser*) { + nodep->iterateChildren(*this); + expectDescriptor(nodep, nodep->filep()); + } + + virtual void visit(AstScCtor* nodep, AstNUser*) { + // Constructor info means the module must remain public + m_modp->modPublic(true); + nodep->iterateChildren(*this); + } + virtual void visit(AstScInt* nodep, AstNUser*) { + // Special class info means the module must remain public + m_modp->modPublic(true); + nodep->iterateChildren(*this); + } + + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + LinkResolveVisitor(AstNetlist* rootp) { + m_ftaskp = NULL; + m_modp = NULL; + m_setRefLvalue = false; + m_beginNum = 0; + m_beginLastp = NULL; + // + rootp->accept(*this); + } + virtual ~LinkResolveVisitor() {} +}; + +//###################################################################### +// LinkBotupVisitor +// Recurses cells backwards, so we can pick up those things that propagate +// from child cells up to the top module. + +class LinkBotupVisitor : public AstNVisitor { +private: + // STATE + AstModule* m_modp; // Current module + //int debug() { return 9; } + + // VISITs + virtual void visit(AstNetlist* nodep, AstNUser*) { + // Iterate modules backwards, in bottom-up order. + nodep->iterateChildrenBackwards(*this); + } + virtual void visit(AstModule* nodep, AstNUser*) { + m_modp = nodep; + nodep->iterateChildren(*this); + m_modp = NULL; + } + virtual void visit(AstCell* nodep, AstNUser*) { + // Parent module inherits child's publicity + if (nodep->modp()->modPublic()) m_modp->modPublic(true); + //** No iteration for speed + } + virtual void visit(AstNodeMath* nodep, AstNUser*) { + // Speedup + } + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + LinkBotupVisitor(AstNetlist* rootp) { + m_modp = NULL; + // + rootp->accept(*this); + } + virtual ~LinkBotupVisitor() {} +}; + +//###################################################################### +// Link class functions + +void V3LinkResolve::linkResolve(AstNetlist* rootp) { + UINFO(4,__FUNCTION__<<": "< + +//============================================================================ + +template class V3List; +template class V3ListEnt; + +template +class V3List { + // List container for linked list of elements of type *T (T is a pointer type) +private: + // STATE + T m_headp; // First element + T m_tailp; // Last element + friend class V3ListEnt; +public: + V3List() + : m_headp(NULL), m_tailp(NULL) {} + ~V3List() {} + // METHODS + T begin() const { return m_headp; } + T end() const { return NULL; } + bool empty() const { return m_headp==NULL; } + void reset() { m_headp=NULL; m_tailp=NULL; } // clear() without walking the list +}; + +//============================================================================ + +template +class V3ListEnt { + // List entry for linked list of elements of type *T (T is a pointer type) +private: + // STATE + T m_nextp; // Pointer to next element, NULL=end + T m_prevp; // Pointer to previous element, NULL=beginning + friend class V3List; + static V3ListEnt* baseToListEnt(void* newbasep, uint32_t offset) { + // "this" must be a element inside of *basep + // Use that to determine a structure offset, then apply to the new base + // to get our new pointer information + return (V3ListEnt*) ( ((uint8_t*)newbasep) + offset); + } +public: + V3ListEnt() + : m_nextp(NULL), m_prevp(NULL) {} + ~V3ListEnt() { +#ifdef VL_DEBUG + // Load bogus pointers so we can catch deletion bugs + m_nextp = (T)1; + m_prevp = (T)1; +#endif + } + T nextp() const { return m_nextp; } + // METHODS + void pushBack (V3List& listr, T newp) { + // "this" must be a element inside of *newp + uint32_t offset = (uint8_t*)(this) - (uint8_t*)(newp); + m_nextp = NULL; + if (!listr.m_headp) listr.m_headp = newp; + m_prevp = listr.m_tailp; + if (m_prevp) baseToListEnt(m_prevp,offset)->m_nextp = newp; + listr.m_tailp = newp; + } + void pushFront (V3List& listr, T newp) { + // "this" must be a element inside of *newp + uint32_t offset = (uint8_t*)(this) - (uint8_t*)(newp); + m_nextp = listr.m_headp; + if (m_nextp) baseToListEnt(m_nextp,offset)->m_prevp = newp; + listr.m_headp = newp; + m_prevp = NULL; + if (!listr.m_tailp) listr.m_tailp = newp; + } + // Unlink from side + void unlink (V3List& listr, T oldp) { + // "this" must be a element inside of *oldp + uint32_t offset = (uint8_t*)(this) - (uint8_t*)(oldp); + if (m_nextp) baseToListEnt(m_nextp,offset)->m_prevp = m_prevp; + else listr.m_tailp = m_prevp; + if (m_prevp) baseToListEnt(m_prevp,offset)->m_nextp = m_nextp; + else listr.m_headp = m_nextp; + m_prevp = m_nextp = NULL; + } +}; + +//============================================================================ + +#endif // Guard diff --git a/src/V3Localize.cpp b/src/V3Localize.cpp new file mode 100644 index 000000000..0d635e882 --- /dev/null +++ b/src/V3Localize.cpp @@ -0,0 +1,253 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Convert BLOCKTEMPs to local variables +// +// 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. +// +//************************************************************************* +// LOCALIZE TRANSFORMATIONS: +// All modules: +// VAR(BLOCKTEMP... +// if only referenced in a CFUNC, make it local to that CFUNC +// VAR(others +// if non-public, set before used, and in signle CFUNC, make it local +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Localize.h" +#include "V3Stats.h" +#include "V3Ast.h" + +//###################################################################### +// Localize base class + +class LocalizeBaseVisitor : public AstNVisitor { +protected: + // NODE STATE + // Cleared on entire tree + // AstVar::userp() -> CFunc which references the variable + // AstVar::user2() -> VarFlags. Flag state + // AstVar::user4() -> AstVarRef*. First place signal set; must be first assignment + + //int debug() { return 9; } + + // TYPES + union VarFlags { + // Per-variable flags + // Used in user()'s so initializes to all zeros + struct { + int m_notOpt:1; // NOT optimizable + int m_notStd:1; // NOT optimizable if a non-blocktemp signal + int m_stdFuncAsn:1; // Found simple assignment + int m_done:1; // Removed + }; + uint32_t m_flags; + VarFlags(AstNode* nodep) { m_flags = nodep->user2(); } + void setNodeFlags(AstNode* nodep) { nodep->user2(m_flags); }; + }; +}; + +//###################################################################### +// Localize class functions + +class LocalizeDehierVisitor : public LocalizeBaseVisitor { +private: + // NODE STATE/TYPES + // See above + + // METHODS + virtual void visit(AstVarRef* nodep, AstNUser*) { + VarFlags flags (nodep->varp()); + if (flags.m_done) { + nodep->hiername(""); // Remove thisp-> + nodep->hierThis(true); + } + } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTRUCTORS + LocalizeDehierVisitor(AstNetlist* nodep) { + nodep->accept(*this); + } + virtual ~LocalizeDehierVisitor() {} +}; + +//###################################################################### +// Localize class functions + +class LocalizeVisitor : public LocalizeBaseVisitor { +private: + // NODE STATE/TYPES + // See above + + // STATE + V3Double0 m_statLocVars; // Statistic tracking + AstCFunc* m_cfuncp; // Current active function + vector m_varps; // List of variables to consider for deletion + + // METHODS + void clearOptimizable(AstVar* nodep, const char* reason) { + UINFO(4," NoOpt "<::iterator it = m_varps.begin(); it != m_varps.end(); ++it) { + AstVar* nodep = *it; + if (nodep->initp()) clearOptimizable(nodep,"HasInitValue"); + if (!VarFlags(nodep).m_stdFuncAsn) clearStdOptimizable(nodep,"NoStdAssign"); + VarFlags flags (nodep); + if ((nodep->isMovableToBlock() // Blocktemp + || !flags.m_notStd) // Or used only in block + && !flags.m_notOpt // Optimizable + && nodep->userp()) { // Single cfunc + // We don't need to test for tracing; it would be in the tracefunc if it was needed + UINFO(4," ModVar->BlkVar "<userp()->castNode()->castCFunc(); + nodep->unlinkFrBack(); + newfuncp->addInitsp(nodep); + // Done + flags.m_done = true; + flags.setNodeFlags(nodep); + } else { + clearOptimizable(nodep, "NotDone"); + } + } + m_varps.clear(); + } + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + AstNode::userClearTree(); // userp() used on entire tree + AstNode::user2ClearTree(); // userp() used on entire tree + AstNode::user3ClearTree(); // userp() used on entire tree + AstNode::user4ClearTree(); // userp() used on entire tree + nodep->iterateChildren(*this); + moveVars(); + } + virtual void visit(AstModule* nodep, AstNUser*) { + // Consumption/generation of a variable, + nodep->iterateChildren(*this); + } + virtual void visit(AstCFunc* nodep, AstNUser*) { + UINFO(4," CFUNC "<argsp()); + searchFuncStmts(nodep->initsp()); + searchFuncStmts(nodep->stmtsp()); + searchFuncStmts(nodep->finalsp()); + nodep->iterateChildren(*this); + m_cfuncp = NULL; + } + void searchFuncStmts(AstNode* nodep) { + // Search for basic assignments to allow moving non-blocktemps + // For now we only find simple assignments not under any other statement. + // This could be more complicated; allow always-set under both branches of a IF. + // If so, check for ArrayRef's and such, as they aren't acceptable. + for (; nodep; nodep=nodep->nextp()) { + if (nodep->castNodeAssign()) { + if (AstVarRef* varrefp = nodep->castNodeAssign()->lhsp()->castVarRef()) { + if (!varrefp->lvalue()) varrefp->v3fatalSrc("LHS assignment not lvalue"); + if (!varrefp->varp()->user4p()) { + UINFO(4," FuncAsn "<varp()->user4p(varrefp); + VarFlags flags (varrefp->varp()); + flags.m_stdFuncAsn = true; + flags.setNodeFlags(varrefp->varp()); + } + } + } + } + } + + virtual void visit(AstVar* nodep, AstNUser*) { + if (!nodep->isSigPublic() + && !nodep->isPrimaryIO() + && !m_cfuncp) { // Not already inside a function + UINFO(4," BLKVAR "<varp()).m_notOpt) { + if (!m_cfuncp) { // Not in function, can't optimize + clearOptimizable(nodep->varp(), "BVnofunc"); + } + else { + // If we're scoping down to it, it isn't really in the same block + if (!nodep->hierThis()) clearOptimizable(nodep->varp(),"HierRef"); + // Allow a variable to appear in only a single function + AstNode* oldfunc = nodep->varp()->userp()->castNode(); + if (!oldfunc) { + UINFO(4," BVnewref "<varp()->userp(m_cfuncp); // Remember where it was used + } else if (m_cfuncp == oldfunc) { + // Same usage + } else { + // Used in multiple functions + clearOptimizable(nodep->varp(),"BVmultiF"); + } + // First varref in function must be assignment found earlier + AstVarRef* firstasn = (AstVarRef*)(nodep->varp()->user4p()); + if (firstasn && nodep!=firstasn) { + clearStdOptimizable(nodep->varp(),"notFirstAsn"); + nodep->varp()->user4p(NULL); + } + } + } + // No iterate; Don't want varrefs under it + } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTRUCTORS + LocalizeVisitor(AstNetlist* nodep) { + m_cfuncp = NULL; + nodep->accept(*this); + } + virtual ~LocalizeVisitor() { + V3Stats::addStat("Optimizations, Vars localized", m_statLocVars); + } +}; + +//###################################################################### +// Localize class functions + +void V3Localize::localizeAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include + +#include "V3Global.h" +#include "V3Name.h" +#include "V3Ast.h" + +//###################################################################### +// Name state, as a visitor of each AstNode + +class NameVisitor : public AstNVisitor { +private: + // NODE STATE + // Cleared on Netlist + // AstCell::user() -> bool. Set true if already processed + // AstScope::user() -> bool. Set true if already processed + // AstVar::user() -> bool. Set true if already processed + // + // AstCell::user2() -> bool. Set true if was privitized + // AstVar::user2() -> bool. Set true if was privitized + + // STATE + AstModule* m_modp; + + //int debug() { return 9; } + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + AstNode::userClearTree(); + AstNode::user2ClearTree(); + nodep->iterateChildren(*this); + } + virtual void visit(AstModule* nodep, AstNUser*) { + //VV***** We reset all userp() on each module!!! + AstNode::userClearTree(); + m_modp = nodep; + nodep->iterateChildren(*this); + } + // Add __PVT__ to names of local signals + virtual void visit(AstVar* nodep, AstNUser*) { + // Don't iterate... Don't need temps for RANGES under the Var. + if (!nodep->user() + && !m_modp->isTop() + && !nodep->isSigPublic() + && !nodep->isTemp()) { // Don't bother to rename internal signals + // Change the name to something private... + string newname = (string)"__PVT__"+nodep->name(); + nodep->name(newname); + nodep->user(1); + nodep->user2(1); + } + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + if (nodep->varp()) { + nodep->varp()->iterateChildren(*this); + nodep->name(nodep->varp()->name()); + } + } + virtual void visit(AstCell* nodep, AstNUser*) { + if (!nodep->user() + && !nodep->modp()->modPublic()) { + // Change the name to something private... + string newname = (string)"__PVT__"+nodep->name(); + nodep->name(newname); + nodep->user(1); + nodep->user2(1); + } + nodep->iterateChildren(*this); + } + virtual void visit(AstScope* nodep, AstNUser*) { + if (!nodep->user()) { + if (nodep->aboveScopep()) nodep->aboveScopep()->iterateChildren(*this); + nodep->aboveCellp()->iterateChildren(*this); + // Always recompute name (as many level above scope may have changed) + // Same formula as V3Scope + nodep->name(nodep->isTop() ? "TOP" + : (nodep->aboveScopep()->name()+"."+nodep->aboveCellp()->name())); + nodep->user(1); + } + } + + //-------------------- + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + NameVisitor(AstNode* nodep) { + nodep->accept(*this); + } + virtual ~NameVisitor() {} +}; + +//###################################################################### +// Name class functions + +void V3Name::nameAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include +#include +#include "V3Number.h" + +//###################################################################### +// Read class functions +// CREATION + +void V3Number::width(int width, bool sized) { + // Set width. Only set m_width here, as we need to tweak vector size + if (width) { m_sized = sized; m_width=width; } + else { m_sized = false; m_width=1; } + if (m_value.size() < (unsigned)(words()+1)) { + m_value.resize(words()+1); + m_valueX.resize(words()+1); + } +} + +void V3Number::init (FileLine* fileline, int swidth) { + m_fileline = fileline; + m_signed = false; + m_autoExtend = false; + width(swidth); + for (int i=0; i32 && olen>7/*10000000 fits in 32 bits, so ok*/) { + m_fileline->v3error("Unsupported: Conversion of decimal number over 32 bits, use hex\n"); + olen=0; + } + olen++; + break; + } + case 'z': { + if (olen) m_fileline->v3error("Multi-digit X/Z/? not legal in decimal constant: "<<*cp); + if (!m_sized) m_fileline->v3error("Unsized X/Z/? not legal in decimal constant: "<<*cp); + olen++; + setAllBitsZ(); + break; + } + case 'x': case '?': { + if (olen) m_fileline->v3error("Multi-digit X/Z/? not legal in decimal constant: "<<*cp); + if (!m_sized) m_fileline->v3error("Unsized X/Z/? not legal in decimal constant: "<<*cp); + olen++; + setAllBitsX(); + break; + } + case '_': break; + default: { + m_fileline->v3error("Illegal character in decimal constant: "<<*cp); + break; + } + } + } + obit = width(); + } + else { + // Convert bin/octal number to hex + for (const char* cp=value_startp+strlen(value_startp)-1; + (cp>=value_startp + && obit<=width()); + cp--) { + if (*cp!='_' && obit>=width()) { + m_fileline->v3error("Too many digits for "<v3error("Illegal character in binary constant: "<<*cp); + } + break; + } + + case 'o': + case 'c': { + switch(tolower(*cp)) { + case '0': setBit(obit++, 0); setBit(obit++, 0); setBit(obit++, 0); break; + case '1': setBit(obit++, 1); setBit(obit++, 0); setBit(obit++, 0); break; + case '2': setBit(obit++, 0); setBit(obit++, 1); setBit(obit++, 0); break; + case '3': setBit(obit++, 1); setBit(obit++, 1); setBit(obit++, 0); break; + case '4': setBit(obit++, 0); setBit(obit++, 0); setBit(obit++, 1); break; + case '5': setBit(obit++, 1); setBit(obit++, 0); setBit(obit++, 1); break; + case '6': setBit(obit++, 0); setBit(obit++, 1); setBit(obit++, 1); break; + case '7': setBit(obit++, 1); setBit(obit++, 1); setBit(obit++, 1); break; + case 'z': setBit(obit++, 'z'); setBit(obit++, 'z'); setBit(obit++, 'z'); break; + case 'x': case '?': + setBit(obit++, 'x'); setBit(obit++, 'x'); setBit(obit++, 'x'); break; + case '_': break; + default: + m_fileline->v3error("Illegal character in octal constant"); + } + break; + } + + case 'h': { + switch(tolower(*cp)) { + case '0': setBit(obit++,0); setBit(obit++,0); setBit(obit++,0); setBit(obit++,0); break; + case '1': setBit(obit++,1); setBit(obit++,0); setBit(obit++,0); setBit(obit++,0); break; + case '2': setBit(obit++,0); setBit(obit++,1); setBit(obit++,0); setBit(obit++,0); break; + case '3': setBit(obit++,1); setBit(obit++,1); setBit(obit++,0); setBit(obit++,0); break; + case '4': setBit(obit++,0); setBit(obit++,0); setBit(obit++,1); setBit(obit++,0); break; + case '5': setBit(obit++,1); setBit(obit++,0); setBit(obit++,1); setBit(obit++,0); break; + case '6': setBit(obit++,0); setBit(obit++,1); setBit(obit++,1); setBit(obit++,0); break; + case '7': setBit(obit++,1); setBit(obit++,1); setBit(obit++,1); setBit(obit++,0); break; + case '8': setBit(obit++,0); setBit(obit++,0); setBit(obit++,0); setBit(obit++,1); break; + case '9': setBit(obit++,1); setBit(obit++,0); setBit(obit++,0); setBit(obit++,1); break; + case 'a': setBit(obit++,0); setBit(obit++,1); setBit(obit++,0); setBit(obit++,1); break; + case 'b': setBit(obit++,1); setBit(obit++,1); setBit(obit++,0); setBit(obit++,1); break; + case 'c': setBit(obit++,0); setBit(obit++,0); setBit(obit++,1); setBit(obit++,1); break; + case 'd': setBit(obit++,1); setBit(obit++,0); setBit(obit++,1); setBit(obit++,1); break; + case 'e': setBit(obit++,0); setBit(obit++,1); setBit(obit++,1); setBit(obit++,1); break; + case 'f': setBit(obit++,1); setBit(obit++,1); setBit(obit++,1); setBit(obit++,1); break; + case 'z': setBit(obit++,'z'); setBit(obit++,'z'); setBit(obit++,'z'); setBit(obit++,'z'); break; + case 'x': case '?': + setBit(obit++,'x'); setBit(obit++,'x'); setBit(obit++,'x'); setBit(obit++,'x'); break; + case '_': break; + default: + m_fileline->v3error("Illegal character in hex constant: "<<*cp); + } + break; + } + default: + m_fileline->v3error("Illegal base character: "<0; bit--) if (num & (VL_ULL(1)<>VL_ULL(32)) & VL_ULL(0xffffffff); + return *this; +} +V3Number& V3Number::setLong(uint32_t value) { + for (int i=0; i=0; --bit) { + if (bitIs0(bit)) out<<'0'; + else if (bitIs1(bit)) out<<'1'; + else if (bitIsZ(bit)) out<<'z'; + else out<<'x'; + } + } + else { + if (prefixed) out<<"h"; + // Always deal with 4 bits at once. Note no 4-state, it's above. + int hexStart = width()-1; + while (hexStart && bitIs0(hexStart)) hexStart--; + while ((hexStart&3)!=3) hexStart++; + for(int bit=hexStart; bit>0; ) { + int v = 0; + if (bitIs1(bit)) v |= 8; bit--; + if (bitIs1(bit)) v |= 4; bit--; + if (bitIs1(bit)) v |= 2; bit--; + if (bitIs1(bit)) v |= 1; bit--; + if (v>=10) out<<(char)('a'+v-10); + else out<<(char)('0'+v); + } + } + return out.str(); +} + +uint32_t V3Number::asInt() const { + UASSERT(!isFourState(),"asInt with 4-state "<<*this); + UASSERT((width()<33 || (width()<65 && m_value[1]==0)), "Value too wide "<<*this); + return m_value[0]; +} + +vlsint32_t V3Number::asSInt() const { + UASSERT(!isFourState(),"asSInt with 4-state "<<*this); + UASSERT((width()<33 || (width()<65 && m_value[1]==0)), "Value too wide "<<*this); + uint32_t signExtend = (-((m_value[0]) & (1UL<<(width()-1)))); + uint32_t extended = m_value[0] | signExtend; + return (vlsint32_t)(extended); +} + +vluint64_t V3Number::asQuad() const { + UASSERT(!isFourState(),"asQuad with 4-state "<<*this); + UASSERT(width()<65, "Value too wide "<<*this); + if (width()<=32) return ((vluint64_t)m_value[0]); + else return ((vluint64_t)m_value[1]<0; bit--) { + if (!bitIs0(bit)) return bit+1; + } + return 1; // one bit even if number is == 0 +} + +uint32_t V3Number::countOnes() const { + int n=0; + for(int bit=0; bitwidth(); bit++) { + if (bitIs1(bit)) n++; + } + return n; +} + +//====================================================================== + +V3Number& V3Number::opBitsNonX (const V3Number& lhs) { // 0/1->1, X/Z->0 + // op i, L(lhs) bit return + setZero(); + for(int bit=0; bitwidth(); bit++) { + if (lhs.bitIs0(bit) || lhs.bitIs1(bit)) { setBit(bit,1); } + } + return *this; +} +V3Number& V3Number::opBitsOne (const V3Number& lhs) { // 1->1, 0/X/Z->0 + // op i, L(lhs) bit return + setZero(); + for(int bit=0; bitwidth(); bit++) { + if (lhs.bitIs1(bit)) { setBit(bit,1); } + } + return *this; +} +V3Number& V3Number::opBitsXZ (const V3Number& lhs) { // 0/1->1, X/Z->0 + // op i, L(lhs) bit return + setZero(); + for(int bit=0; bitwidth(); bit++) { + if (lhs.bitIsXZ(bit)) { setBit(bit,1); } + } + return *this; +} + +//====================================================================== +// Operators - Simple per-bit logical ops + +V3Number& V3Number::opRedOr (const V3Number& lhs) { + // op i, 1 bit return + char outc = 0; + for(int bit=0; bitwidth(); bit++) { + if (lhs.bitIs0(bit)) { setBit(bit,1); } + else if (lhs.bitIsXZ(bit)) { setBit(bit,'x'); } + } + return *this; +} + +V3Number& V3Number::opAnd (const V3Number& lhs, const V3Number& rhs) { + // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. + setZero(); + for(int bit=0; bitwidth(); bit++) { + if (lhs.bitIs1(bit) && rhs.bitIs1(bit)) { setBit(bit,1); } + else if (lhs.bitIs0(bit) || rhs.bitIs0(bit)) ; // 0 + else { setBit(bit,'x'); } + } + return *this; +} + +V3Number& V3Number::opOr (const V3Number& lhs, const V3Number& rhs) { + // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. + setZero(); + for(int bit=0; bitwidth(); bit++) { + if (lhs.bitIs1(bit) || rhs.bitIs1(bit)) { setBit(bit,1); } + else if (lhs.bitIs0(bit) && rhs.bitIs0(bit)) ; // 0 + else { setBit(bit,'x'); } + } + return *this; +} + +V3Number& V3Number::opChangeXor (const V3Number& lhs, const V3Number& rhs) { + // 32 bit result + opEq(lhs,rhs); + return *this; +} + +V3Number& V3Number::opXor (const V3Number& lhs, const V3Number& rhs) { + // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. + setZero(); + for(int bit=0; bitwidth(); bit++) { + if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { setBit(bit,1); } + else if (lhs.bitIs0(bit) && rhs.bitIs1(bit)) { setBit(bit,1); } + else if (lhs.bitIsXZ(bit) && rhs.bitIsXZ(bit)) { setBit(bit,'x'); } + // else zero + } + return *this; +} + +V3Number& V3Number::opXnor (const V3Number& lhs, const V3Number& rhs) { + // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. + setZero(); + for(int bit=0; bitwidth(); bit++) { + if (lhs.bitIs1(bit) && rhs.bitIs1(bit)) { setBit(bit,1); } + else if (lhs.bitIs0(bit) && rhs.bitIs0(bit)) { setBit(bit,1); } + else if (lhs.bitIsXZ(bit) && rhs.bitIsXZ(bit)) { setBit(bit,'x'); } + // else zero + } + return *this; +} + +V3Number& V3Number::opConcat (const V3Number& lhs, const V3Number& rhs) { + setZero(); + if (!lhs.sized()) m_fileline->v3error("Unsized constants not allowed in concatenations: "<v3error("Unsized constants not allowed in concatenations: "<v3error("Unsized constants not allowed in concatenations: "<8192) m_fileline->v3fatal("More then a 8k bit replication is probably wrong: "<width() != rhs.width()) return false; + for (int bit=0; bitwidth(),rhs.width()); bit++) { + if (this->bitIs(bit) != rhs.bitIs(bit)) { return false; } + } + return true; +} + +V3Number& V3Number::opCaseEq (const V3Number& lhs, const V3Number& rhs) { + return setSingleBits(lhs.isCaseEq(rhs) ? 1:0); +} + +V3Number& V3Number::opCaseNeq (const V3Number& lhs, const V3Number& rhs) { + // i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend. + char outc = 0; + for (int bit=0; bit - + else if (lhs.bitIs1Extend(mbit) && rhs.bitIs0(mbit)) { outc=0; } // - !> + + else { + // both positive or negative, normal > + for (int bit=0; bitwidth(); bit++) { + setBit(bit,lhs.bitIs(bit + rhsval)); + } + return *this; +} + +V3Number& V3Number::opShiftRS (const V3Number& lhs, const V3Number& rhs) { + // L(lhs) bit return + // The spec says a unsigned >>> still acts as a normal >>. + // We presume it is signed; as that's V3Signed's job to convert to opShiftR + if (rhs.isFourState()) return setAllBitsX(); + setZero(); + uint32_t rhsval = rhs.asInt(); + for (int bit=0; bitwidth(); bit++) { + setBit(bit,lhs.bitIsExtend(bit + rhsval)); + } + return *this; +} + +V3Number& V3Number::opShiftL (const V3Number& lhs, const V3Number& rhs) { + // L(lhs) bit return + if (rhs.isFourState()) return setAllBitsX(); + uint32_t rhsval = rhs.asInt(); + setZero(); + for (int bit=0; bitwidth(); bit++) { + if (bit >= (int)rhsval) { + setBit(bit,lhs.bitIs(bit - rhsval)); + } + } + return *this; +} + +//====================================================================== +// Ops - Arithmetic + +V3Number& V3Number::opUnaryMin (const V3Number& lhs) { + // op i, L(lhs) bit return + if (lhs.isFourState()) return setAllBitsX(); + V3Number notlhs (lhs.m_fileline, width()); + notlhs.opNot(lhs); + V3Number one (lhs.m_fileline, width(), 1); + opAdd(notlhs,one); + return *this; +} +V3Number& V3Number::opAdd (const V3Number& lhs, const V3Number& rhs) { + // i op j, max(L(lhs),L(rhs)) bit return, if any 4-state, 4-state return + if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); + setZero(); + // Addem + int carry=0; + for (int bit=0; bitwidth(); bit++) { + int sum = ((lhs.bitIs1(bit)?1:0) + (rhs.bitIs1(bit)?1:0) + carry); + if (sum & 1) { + setBit(bit,1); + } + carry = (sum >= 2); + } + return *this; +} +V3Number& V3Number::opSub (const V3Number& lhs, const V3Number& rhs) { + // i op j, max(L(lhs),L(rhs)) bit return, if any 4-state, 4-state return + if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); + V3Number negrhs (rhs.m_fileline, rhs.width()); + negrhs.opUnaryMin(rhs); + return opAdd(lhs, negrhs); +} +V3Number& V3Number::opMul (const V3Number& lhs, const V3Number& rhs) { + // i op j, max(L(lhs),L(rhs)) bit return, if any 4-state, 4-state return + if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); + setZero(); + if (width() <= 64) { + setQuad(lhs.asQuad() * rhs.asQuad()); + opCleanThis(); // Mult produces extra bits in result + } else { + for (int lword=0; lwordwords(); qword++) { + mul += (vluint64_t)(m_value[qword]); + m_value[qword] = (mul & VL_ULL(0xffffffff)); + mul = (mul >> VL_ULL(32)) & VL_ULL(0xffffffff); + } + } + } + opCleanThis(); // Mult produces extra bits in result + } + return *this; +} +V3Number& V3Number::opMulS (const V3Number& lhs, const V3Number& rhs) { + // Signed multiply + if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); + V3Number lhsNoSign = lhs; if (lhs.isNegative()) lhsNoSign.opUnaryMin(lhs); + V3Number rhsNoSign = rhs; if (rhs.isNegative()) rhsNoSign.opUnaryMin(rhs); + V3Number qNoSign = opMul(lhsNoSign,rhsNoSign); + if (lhs.isNegative() && !rhs.isNegative() + || !lhs.isNegative() && rhs.isNegative()) { + opUnaryMin(qNoSign); + } else { + opAssign(qNoSign); + } + return *this; +} +V3Number& V3Number::opDiv (const V3Number& lhs, const V3Number& rhs) { + // i op j, max(L(lhs),L(rhs)) bit return, if any 4-state, 4-state return + if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); + if (lhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large / math not implemented yet: "<<*this); + if (rhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large / math not implemented yet: "<<*this); + setQuad(lhs.asQuad() / rhs.asQuad()); + return *this; +} +V3Number& V3Number::opDivS (const V3Number& lhs, const V3Number& rhs) { + // Signed divide + if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); + V3Number lhsNoSign = lhs; if (lhs.isNegative()) lhsNoSign.opUnaryMin(lhs); + V3Number rhsNoSign = rhs; if (rhs.isNegative()) rhsNoSign.opUnaryMin(rhs); + V3Number qNoSign = opDiv(lhsNoSign,rhsNoSign); + if (lhs.isNegative() && !rhs.isNegative() + || !lhs.isNegative() && rhs.isNegative()) { + opUnaryMin(qNoSign); + } else { + opAssign(qNoSign); + } + return *this; +} +V3Number& V3Number::opModDiv (const V3Number& lhs, const V3Number& rhs) { + // i op j, max(L(lhs),L(rhs)) bit return, if any 4-state, 4-state return + if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); + if (lhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large % math not implemented yet: "<<*this); + if (rhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large % math not implemented yet: "<<*this); + setQuad(lhs.asQuad() % rhs.asQuad()); + return *this; +} +V3Number& V3Number::opModDivS (const V3Number& lhs, const V3Number& rhs) { + // Signed moddiv + if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); + V3Number lhsNoSign = lhs; if (lhs.isNegative()) lhsNoSign.opUnaryMin(lhs); + V3Number rhsNoSign = rhs; if (rhs.isNegative()) rhsNoSign.opUnaryMin(rhs); + V3Number qNoSign = opModDiv(lhsNoSign,rhsNoSign); + if (lhs.isNegative()) { // Just lhs' sign (*DIFFERENT FROM PERL, which uses rhs sign*) + opUnaryMin(qNoSign); + } else { + opAssign(qNoSign); + } + return *this; +} +V3Number& V3Number::opPow (const V3Number& lhs, const V3Number& rhs) { + // L(i) bit return, if any 4-state, 4-state return + if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); + if (lhs.isEqZero()) return setZero(); + // We may want to special case when the lhs is 2, so we can get larger outputs + if (lhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large >64bit ** math not implemented yet: "<<*this); + if (rhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large >64bit ** math not implemented yet: "<<*this); + setZero(); + m_value[0] = 1; + V3Number power (lhs.m_fileline, width()); power.opAssign(lhs); + for (int bit=0; bit0) { // power = power*power + V3Number lastPower (lhs.m_fileline, width()); lastPower.opAssign(power); + power.opMul(lastPower, lastPower); + } + if (rhs.bitIs1(bit)) { // out *= power + V3Number lastOut (lhs.m_fileline, width()); lastOut.opAssign(*this); + this->opMul(lastOut, power); + //UINFO(0, "pow "<width(); bit++) { + setBit(bit,lhs.bitIs(bit)); + } + return *this; +} + +V3Number& V3Number::opExtendS (const V3Number& lhs) { + // Note may be a width change during the sign extension + setZero(); + for(int bit=0; bitwidth(); bit++) { + setBit(bit,lhs.bitIsExtend(bit)); + } + return *this; +} + +V3Number& V3Number::opClean (const V3Number& lhs, uint32_t bits) { + return opRange(lhs, bits-1, 0); +} + +void V3Number::opCleanThis() { + // Clean in place number + if (uint32_t okbits = (width() & 31)) { + m_value[words()-1] &= ((1UL<width(); bit++) { + if (ibit>=0 && ibitwidth(); bit++) { + if (if0s.bitIs1(bit) && if1s.bitIs1(bit)) { setBit(bit,1); } + else if (if0s.bitIs0(bit) && if1s.bitIs0(bit)) { setBit(bit,0); } + else setBit(bit,'x'); + } + } + return *this; +} diff --git a/src/V3Number.h b/src/V3Number.h new file mode 100644 index 000000000..9dca15811 --- /dev/null +++ b/src/V3Number.h @@ -0,0 +1,205 @@ +// $Id$ //-*- C++ -*- +//************************************************************************* +// DESCRIPTION: Verilator: Large 4-state numbers +// +// 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 _V3NUMBER_H_ +#define _V3NUMBER_H_ 1 +#include "config.h" +#include + +#include "V3Error.h" + +//============================================================================ + +class V3Number { + // Large 4-state number handling + int m_width; // Width as specified/calculated. + bool m_sized:1; // True if the user specified the width, else we track it. + bool m_signed:1; // True if signed value + bool m_autoExtend:1; // True if SystemVerilog extend-to-any-width + FileLine* m_fileline; + vector m_value; // The Value, with bit 0 being in bit 0 of this vector (unless X/Z) + vector m_valueX; // Each bit is true if it's X or Z, 10=z, 11=x + // METHODS + void init(FileLine* fileline, int width); + V3Number& setZero(); + V3Number& setSingleBits(char value); + void opCleanThis(); +public: + FileLine* fileline() const { return m_fileline; } + void fileline(FileLine* fl) { m_fileline=fl; } + V3Number& setQuad(vluint64_t value); + V3Number& setLong(uint32_t value); + void setBit (int bit, char value) { // Note must be pre-zeroed! + if (bit>=m_width) return; + if (value=='0'||value==0) m_value [bit/32] &= ~(1UL<<(bit&31)); + else { + if (value=='1'||value=='x'||value==1||value==3) m_value [bit/32] |= (1UL<<(bit&31)); + if (value=='z'||value=='x'||value==2||value==3) m_valueX[bit/32] |= (1UL<<(bit&31)); + } + } +private: + char bitIs (int bit) const { + if (bit>=m_width) { + bit = m_width-1; + // We never sign extend + return ( "00zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0) + | ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); + } + return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0) + | ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); } + char bitIsExtend (int bit) const { + if (bit>=m_width) { + bit = m_width-1; + // We do sign extend + return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0) + | ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); + } + return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0) + | ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); } + bool bitIs0 (int bit) const { + if (bit>=m_width) return !bitIsXZ(m_width-1); + return ( (m_value[bit/32] & (1UL<<(bit&31)))==0 && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); } + bool bitIs1 (int bit) const { + if (bit>=m_width) return false; + return ( (m_value[bit/32] & (1UL<<(bit&31))) && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); } + bool bitIs1Extend (int bit) const { + if (bit>=m_width) return bitIs1Extend(m_width-1); + return ( (m_value[bit/32] & (1UL<<(bit&31))) && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); } + bool bitIsX (int bit) const { + if (bit>=m_width) return bitIsZ(m_width-1); + return ( (m_value[bit/32] & (1UL<<(bit&31))) && (m_valueX[bit/32] & (1UL<<(bit&31))) ); } + bool bitIsXZ(int bit) const { + if (bit>=m_width) return bitIsXZ(m_width-1); + return ( (m_valueX[bit/32] & (1UL<<(bit&31))) ); + } + bool bitIsZ (int bit) const { + if (bit>=m_width) return bitIsZ(m_width-1); + return ( (~m_value[bit/32] & (1UL<<(bit&31))) && (m_valueX[bit/32] & (1UL<<(bit&31))) ); } + int words() const { return ((width()+31)/32); } + +public: + class VerilogString {}; // for creator type-overload selection + // CONSTRUCTORS + V3Number(FileLine* fileline) { init(fileline, 1); } + V3Number(FileLine* fileline, int width) { init(fileline, width); }; // 0=unsized + V3Number(FileLine* fileline, int width, uint32_t value) { init(fileline, width); m_value[0]=value; } + V3Number(FileLine* fileline, const char* source); // Create from a verilog 32'hxxxx number. + V3Number(VerilogString, FileLine* fileline, const string& vvalue); + + // SETTERS + V3Number& setAllBitsX(); + V3Number& setAllBitsZ(); + + // ACCESSORS + string ascii(bool prefixed=true, bool cleanVerilog=false) const; + int width() const { return m_width; } + int minWidth() const; // Minimum width that can represent this number (~== log2(num)+1) + bool sized() const { return m_sized; } + bool autoExtend() const { return m_autoExtend; } + bool isSigned() const { return m_signed; } // Only correct for parsing of numbers from strings, otherwise not used (use AstConst::isSigned()) + bool isNegative() const { return bitIs1(width()-1); } + bool isFourState() const { for (int i=0;i1, X/Z->0 + V3Number& opBitsOne (const V3Number& lhs); // 1->1, 0/X/Z->0 + V3Number& opBitsXZ (const V3Number& lhs); // 0/1->0, X/Z->1 + // + V3Number& opAssign (const V3Number& lhs); + V3Number& opExtendS (const V3Number& lhs); // Sign extension + V3Number& opRedOr (const V3Number& lhs); + V3Number& opRedAnd (const V3Number& lhs); + V3Number& opRedXor (const V3Number& lhs); + V3Number& opRedXnor (const V3Number& lhs); + V3Number& opCountOnes(const V3Number& lhs); + V3Number& opIsUnknown(const V3Number& lhs); + V3Number& opOneHot (const V3Number& lhs); + V3Number& opOneHot0 (const V3Number& lhs); + V3Number& opClean (const V3Number& lhs, uint32_t bits); + V3Number& opConcat (const V3Number& lhs, const V3Number& rhs); + V3Number& opRepl (const V3Number& lhs, const V3Number& rhs); + V3Number& opRepl (const V3Number& lhs, uint32_t rhs); + V3Number& opRange (const V3Number& lhs, const V3Number& rhs, const V3Number& ths); + V3Number& opRange (const V3Number& lhs, uint32_t rhs, uint32_t ths); + V3Number& opCond (const V3Number& lhs, const V3Number& rhs, const V3Number& ths); + V3Number& opCaseEq (const V3Number& lhs, const V3Number& rhs); + V3Number& opCaseNeq (const V3Number& lhs, const V3Number& rhs); + // "standard" math + V3Number& opNot (const V3Number& lhs); + V3Number& opLogNot (const V3Number& lhs); + V3Number& opLogAnd (const V3Number& lhs, const V3Number& rhs); + V3Number& opLogOr (const V3Number& lhs, const V3Number& rhs); + V3Number& opUnaryMin(const V3Number& lhs); + V3Number& opAdd (const V3Number& lhs, const V3Number& rhs); + V3Number& opSub (const V3Number& lhs, const V3Number& rhs); + V3Number& opMul (const V3Number& lhs, const V3Number& rhs); + V3Number& opMulS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opDiv (const V3Number& lhs, const V3Number& rhs); + V3Number& opDivS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opModDiv (const V3Number& lhs, const V3Number& rhs); + V3Number& opModDivS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opPow (const V3Number& lhs, const V3Number& rhs); + V3Number& opPowS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opAnd (const V3Number& lhs, const V3Number& rhs); + V3Number& opChangeXor(const V3Number& lhs, const V3Number& rhs); + V3Number& opXor (const V3Number& lhs, const V3Number& rhs); + V3Number& opXnor (const V3Number& lhs, const V3Number& rhs); + V3Number& opOr (const V3Number& lhs, const V3Number& rhs); + V3Number& opShiftR (const V3Number& lhs, const V3Number& rhs); + V3Number& opShiftRS (const V3Number& lhs, const V3Number& rhs); // Arithmetic w/carry + V3Number& opShiftL (const V3Number& lhs, const V3Number& rhs); + // Comparisons + V3Number& opEq (const V3Number& lhs, const V3Number& rhs); + V3Number& opNeq (const V3Number& lhs, const V3Number& rhs); + V3Number& opGt (const V3Number& lhs, const V3Number& rhs); + V3Number& opGtS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opGte (const V3Number& lhs, const V3Number& rhs); + V3Number& opGteS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opLt (const V3Number& lhs, const V3Number& rhs); + V3Number& opLtS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opLte (const V3Number& lhs, const V3Number& rhs); + V3Number& opLteS (const V3Number& lhs, const V3Number& rhs); // Signed + +}; +inline ostream& operator<<(ostream& os, V3Number rhs) { return os< +#include +#include +#include +#include "V3Number.h" + +void test(string lhss, string op, string rhss, string exps) { + char* l1 = strdup(lhss.c_str()); + char* r1 = strdup(rhss.c_str()); + char* e1 = strdup(exps.c_str()); + + V3Number lhnum (new FileLine ("ck",__LINE__), l1); + V3Number rhnum (new FileLine ("ck",__LINE__), r1); + V3Number expnum (new FileLine("ck",__LINE__), e1); + + V3Number gotnum (new FileLine("ck",__LINE__), expnum.width()); + + if (op=="redOr") gotnum.opRedOr (lhnum); + else if (op=="redAnd") gotnum.opRedAnd (lhnum); + else if (op=="redXor") gotnum.opRedXor (lhnum); + else if (op=="redXnor") gotnum.opRedXnor (lhnum); + else if (op=="concat") gotnum.opConcat (lhnum,rhnum); + else if (op=="repl") gotnum.opRepl (lhnum,rhnum); + else if (op=="~") gotnum.opNot (lhnum); + else if (op=="!") gotnum.opLogNot (lhnum); + else if (op=="unaryMin") gotnum.opUnaryMin (lhnum); + else if (op=="+") gotnum.opAdd (lhnum,rhnum); + else if (op=="-") gotnum.opSub (lhnum,rhnum); + else if (op=="*") gotnum.opMul (lhnum,rhnum); + else if (op=="/") gotnum.opDiv (lhnum,rhnum); + else if (op=="%") gotnum.opModDiv (lhnum,rhnum); + else if (op=="&") gotnum.opAnd (lhnum,rhnum); + else if (op=="|") gotnum.opOr (lhnum,rhnum); + else if (op=="<") gotnum.opLt (lhnum,rhnum); + else if (op==">") gotnum.opGt (lhnum,rhnum); + else if (op==">>") gotnum.opShiftR (lhnum,rhnum); + else if (op=="<<") gotnum.opShiftL (lhnum,rhnum); + else if (op=="==") gotnum.opEq (lhnum,rhnum); + else if (op=="===") gotnum.opCaseEq (lhnum,rhnum); + else if (op=="!=") gotnum.opNeq (lhnum,rhnum); + else if (op=="<=") gotnum.opLte (lhnum,rhnum); + else if (op==">=") gotnum.opGte (lhnum,rhnum); + else if (op=="&&") gotnum.opLogAnd (lhnum,rhnum); + else if (op=="||") gotnum.opLogOr (lhnum,rhnum); + else v3fatalSrc("Bad opcode: "< +#include +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Options.h" +#include "V3Error.h" +#include "V3File.h" +#include "V3PreShell.h" + +#include "config_rev.h" + +//###################################################################### +// V3 Internal state + +struct V3OptionsImp { + // TYPES + typedef std::map > DirMap; // Directory listing + + // STATE + list m_allArgs; // List of every argument encountered + list m_incDirs; // Include directories (ordered) + set m_incDirSet; // Include directories (for removing duplicates) + set m_libExts; // Library extensions + DirMap m_dirMap; // Directory listing + + // ACCESSOR METHODS + void addIncDir(const string& incdir) { + if (m_incDirSet.find(incdir) == m_incDirSet.end()) { + m_incDirSet.insert(incdir); + m_incDirs.push_back(incdir); + } + } + void addLibExt(const string& libext) { + if (m_libExts.find(libext) == m_libExts.end()) { + m_libExts.insert(libext); + } + } + V3OptionsImp() {} +}; + +void V3Options::addIncDir(const string& incdir) { + m_impp->addIncDir(incdir); +} +void V3Options::addLibExt(const string& libext) { + m_impp->addLibExt(libext); +} +void V3Options::addDefine(const string& defline) { + // Split +define+foo=value into the appropriate parts and parse + string def = defline; + string value; + string::size_type pos; + if ( ((pos=defline.find("+")) != string::npos) + || ((pos=defline.find("=")) != string::npos)) { + value = def.substr(pos+1); + def.erase(pos); + } + V3PreShell::define(def,value); +} + +void V3Options::addCppFile(const string& filename) { + if (m_cppFiles.find(filename) == m_cppFiles.end()) { + m_cppFiles.insert(filename); + } +} +void V3Options::addLibraryFile(const string& filename) { + if (m_libraryFiles.find(filename) == m_libraryFiles.end()) { + m_libraryFiles.insert(filename); + } +} +void V3Options::addArg(const string& arg) { + m_impp->m_allArgs.push_back(arg); +} + +string V3Options::allArgsString() { + string out; + for (list::iterator it=m_impp->m_allArgs.begin(); it!=m_impp->m_allArgs.end(); ++it) { + if (out != "") out += " "; + out += *it; + } + return out; +} + +//###################################################################### +// File searching + +string V3Options::filenameFromDirBase (const string& dir, const string& basename) { + // Don't return ./{filename} because if filename was absolute, that makes it relative + if (dir == ".") return basename; + else return dir+"/"+basename; +} + +string V3Options::filenameDir (const string& filename) { + string::size_type pos; + if ((pos = filename.rfind("/")) != string::npos) { + return filename.substr(0,pos); + } else { + return "."; + } +} + +string V3Options::filenameNonDir (const string& filename) { + string::size_type pos; + if ((pos = filename.rfind("/")) != string::npos) { + return filename.substr(pos+1); + } else { + return filename; + } +} + +string V3Options::filenameNonExt (const string& filename) { + string base = filenameNonDir(filename); + string::size_type pos; + if ((pos = base.find(".")) != string::npos) { + base.erase(pos); + } + return base; +} + +bool V3Options::fileStatNormal(const string& filename) { + struct stat m_stat; // Stat information + int err = stat(filename.c_str(), &m_stat); + if (err!=0) return false; + if (S_ISDIR(m_stat.st_mode)) return false; + return true; +} + +string V3Options::fileExists (const string& filename) { + // Surprisingly, for VCS and other simulators, this process + // is quite slow; presumably because of re-reading each directory + // many times. So we read a whole dir at once and cache it + + string dir = filenameDir(filename); + string basename = filenameNonDir(filename); + + V3OptionsImp::DirMap::iterator diriter = m_impp->m_dirMap.find(dir); + if (diriter == m_impp->m_dirMap.end()) { + // Read the listing + m_impp->m_dirMap.insert(make_pair(dir, set() )); + diriter = m_impp->m_dirMap.find(dir); + + set* setp = &(diriter->second); + + if (DIR* dirp = opendir(dir.c_str())) { + while (struct dirent* direntp = readdir(dirp)) { + + setp->insert(direntp->d_name); + } + closedir(dirp); + } + } + // Find it + set* filesetp = &(diriter->second); + set::iterator fileiter = filesetp->find(basename); + if (fileiter == filesetp->end()) { + return ""; // Not found + } + // Check if it is a directory, ignore if so + string filenameOut = filenameFromDirBase (dir, basename); + if (!fileStatNormal(filenameOut)) return ""; // Directory + return filenameOut; +} + +string V3Options::filePath (FileLine* fl, const string& modname, const string& errmsg) { + // Find a filename to read the specified module name, + // using the incdir and libext's. + // Return "" if not found. + for (list::iterator dirIter=m_impp->m_incDirs.begin(); dirIter!=m_impp->m_incDirs.end(); ++dirIter) { + for (set::iterator extIter=m_impp->m_libExts.begin(); extIter!=m_impp->m_libExts.end(); ++extIter) { + string fn = filenameFromDirBase(*dirIter,modname+*extIter); + string exists = fileExists(fn); + if (exists!="") { + // Strip ./, it just looks ugly + if (exists.substr(0,2)=="./") exists.erase(0,2); + return exists; + } + } + } + + // Warn and return not found + fl->v3error(errmsg+modname); + static bool shown_notfound_msg = false; + if (!shown_notfound_msg) { + shown_notfound_msg = true; + if (m_impp->m_incDirs.empty()) { + fl->v3error("This may be because there's no search path specified with -I."<v3error("Looked in:"<::iterator dirIter=m_impp->m_incDirs.begin(); dirIter!=m_impp->m_incDirs.end(); ++dirIter) { + for (set::iterator extIter=m_impp->m_libExts.begin(); extIter!=m_impp->m_libExts.end(); ++extIter) { + string fn = filenameFromDirBase(*dirIter,modname+*extIter); + fl->v3error(" "<v3fatal ("Invalid Option: "<v3fatal("Unknown warning disabled: "<v3fatal("Unknown warning specified: "<v3fatal("Unknown setting for -x-assign: "<v3fatal ("Invalid Option: "<v3fatal ("Top filename specified twice: "< ifp (V3File::new_ifstream(filename)); + if (ifp->fail()) { + fl->v3error("Cannot open -f command file: "+filename); + return; + } + + string whole_file; + string::size_type pos; + while (!ifp->eof()) { + string line; + getline(*ifp, line); + // Strip simple comments + if ((pos=line.find("//")) != string::npos) { + line.erase(pos); + } + whole_file += line + " "; + } + whole_file += "\n"; // So string match below is simplified + + fl = new FileLine(filename, 0); + + // Split into argument list and process + // Note we don't respect quotes. It seems most simulators dont. + // Woez those that expect it; we'll at least complain. + if ((pos=whole_file.find("\"")) != string::npos) { + fl->v3error("Double quotes in -f files cause unspecified behavior."); + } + + // Strip off arguments and parse into words + vector args; + string::size_type startpos = 0; + while (startpos < whole_file.length()) { + while (isspace(whole_file[startpos])) startpos++; + string::size_type endpos = startpos; + while (endpos < whole_file.length() && !isspace(whole_file[endpos])) endpos++; + if (startpos != endpos) { + string arg (whole_file, startpos, endpos-startpos); + args.reserve(args.size()+1); + args.push_back(arg); + } + startpos = endpos; + } + + // Convert to argv style arg list and parse them + char* argv [args.size()+1]; + for (unsigned i=0; i 0; + m_oAcycSimp = flag; + m_oCase = flag; + m_oCombine = flag; + m_oConst = flag; + m_oExpand = flag; + m_oGate = flag; + m_oInline = flag; + m_oLife = flag; + m_oLifePost = flag; + m_oLocalize = flag; + m_oReorder = flag; + m_oSplit = flag; + m_oSubst = flag; + m_oSubstConst = flag; + m_oTable = flag; + // And set specific optimization levels + if (level >= 3) { + m_inlineMult = -1; // Maximum inlining + } +} diff --git a/src/V3Options.h b/src/V3Options.h new file mode 100644 index 000000000..3adfd6bbd --- /dev/null +++ b/src/V3Options.h @@ -0,0 +1,206 @@ +// $Id$ //-*- C++ -*- +//************************************************************************* +// DESCRIPTION: Verilator: Command line options +// +// 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 _V3OPTIONS_H_ +#define _V3OPTIONS_H_ 1 + +#include "config.h" +#include +#include + +#include "V3Global.h" + +//###################################################################### +// V3Options - Command line options + +class V3OptionsImp; +class FileLine; + +typedef set V3StringSet; + +class V3Options { + // MEMBERS (general options) + V3OptionsImp* m_impp; // Slow hidden options + + V3StringSet m_cppFiles; // C++ files to link against + V3StringSet m_libraryFiles; // Verilog -v files + + bool m_preprocOnly; // main switch: -E + bool m_depend; // main switch: -MMD + bool m_assert; // main switch: --assert + bool m_coverageLine; // main switch: --coverage-block + bool m_coverageUser; // main switch: --coverage-func + bool m_dumpTree; // main switch: --dump-tree + bool m_exe; // main switch: --exe + bool m_ignc; // main switch: --ignc + bool m_l2Name; // main switch: --l2name + bool m_outFormatOk; // main switch: --cc, --sc or --sp was specified + bool m_pins64; // main switch: --pins64 + bool m_profileCFuncs;// main switch: --profile-cfuncs + bool m_psl; // main switch: --psl + bool m_public; // main switch: --public + bool m_systemC; // main switch: --sc: System C instead of simple C++ + bool m_skipIdentical;// main switch: --skip-identical + bool m_systemPerl; // main switch: --sp: System Perl instead of SystemC (m_systemC also set) + bool m_stats; // main switch: --stats + bool m_trace; // main switch: --trace + bool m_traceDups; // main switch: --trace-dups + bool m_underlineZero;// main switch: --underline-zero + + int m_inlineMult; // main switch: --inline-mult + int m_outputSplit; // main switch: --output-split + int m_unrollCount; // main switch: --unroll-count + int m_unrollStmts; // main switch: --unroll-stmts + + string m_bin; // main switch: --bin {binary} + string m_flags; // main switch: -f {name} + string m_top; // main switch: Top .v file name + string m_makeDir; // main switch: -Mdir + string m_prefix; // main switch: --prefix + string m_modPrefix; // main switch: --mod-prefix + string m_xAssign; // main switch: --x-assign + + // MEMBERS (optimizations) + // // main switch: -Op: --public + bool m_oAcycSimp; // main switch: -Oy: acyclic pre-optimizations + bool m_oCase; // main switch: -Oe: case tree conversion + bool m_oCombine; // main switch: -Ob: common icode packing + bool m_oConst; // main switch: -Oc: constant folding + bool m_oExpand; // main switch: -Ox: expansion of C macros + bool m_oGate; // main switch: -Og: gate wire elimination + bool m_oLife; // main switch: -Ol: variable lifetime + bool m_oLifePost; // main switch: -Ot: delayed assignment elimination + bool m_oLocalize; // main switch: -Oz: convert temps to local variables + bool m_oInline; // main switch: -Oi: module inlining + bool m_oReorder; // main switch: -Or: reorder assignments in blocks + bool m_oSplit; // main switch: -Os: always assignment splitting + bool m_oSubst; // main switch: -Ou: substitute expression temp values + bool m_oSubstConst; // main switch: -Ok: final constant substitution + bool m_oTable; // main switch: -Oa: lookup table creation + + private: + // METHODS + void addArg(const string& incdir); + void addIncDir(const string& incdir); + void addLibExt(const string& libext); + void addDefine(const string& defline); + void optimize(int level); + void coverage(bool flag) { m_coverageLine = m_coverageUser = flag; } + bool onoff(const char* sw, const char* arg, bool& flag); + + public: + // CREATORS + V3Options(); + ~V3Options(); + void setDebugMode(int level); + + // METHODS + void addCppFile(const string& filename); + void addLibraryFile(const string& filename); + + // ACCESSORS (options) + const string& top() const { return m_top; } + bool preprocOnly() const { return m_preprocOnly; } + bool depend() const { return m_depend; } + bool underlineZero() const { return m_underlineZero; } + string bin() const { return m_bin; } + string flags() const { return m_flags; } + bool systemC() const { return m_systemC; } + bool systemPerl() const { return m_systemPerl; } + bool skipIdentical() const { return m_skipIdentical; } + bool stats() const { return m_stats; } + bool assertOn() const { return m_assert; } // assertOn as "assert" may be defined + bool coverage() const { return m_coverageUser || m_coverageLine; } + bool coverageLine() const { return m_coverageLine; } + bool coverageUser() const { return m_coverageUser; } + bool dumpTree() const { return m_dumpTree; } + bool exe() const { return m_exe; } + bool trace() const { return m_trace; } + bool traceDups() const { return m_traceDups; } + bool outFormatOk() const { return m_outFormatOk; } + bool pins64() const { return m_pins64; } + bool profileCFuncs() const { return m_profileCFuncs; } + bool psl() const { return m_psl; } + bool allPublic() const { return m_public; } + bool l2Name() const { return m_l2Name; } + bool ignc() const { return m_ignc; } + + int inlineMult() const { return m_inlineMult; } + int outputSplit() const { return m_outputSplit; } + int unrollCount() const { return m_unrollCount; } + int unrollStmts() const { return m_unrollStmts; } + + string makeDir() const { return m_makeDir; } + string prefix() const { return m_prefix; } + string modPrefix() const { return m_modPrefix; } + string xAssign() const { return m_xAssign; } + const V3StringSet& cppFiles() const { return m_cppFiles; } + const V3StringSet& libraryFiles() const { return m_libraryFiles; } + + // ACCESSORS (optimization options) + bool oAcycSimp() const { return m_oAcycSimp; } + bool oCase() const { return m_oCase; } + bool oCombine() const { return m_oCombine; } + bool oConst() const { return m_oConst; } + bool oExpand() const { return m_oExpand; } + bool oGate() const { return m_oGate; } + bool oDup() const { return oLife(); } + bool oLife() const { return m_oLife; } + bool oLifePost() const { return m_oLifePost; } + bool oLocalize() const { return m_oLocalize; } + bool oInline() const { return m_oInline; } + bool oReorder() const { return m_oReorder; } + bool oSplit() const { return m_oSplit; } + bool oSubst() const { return m_oSubst; } + bool oSubstConst() const { return m_oSubstConst; } + bool oTable() const { return m_oTable; } + + // METHODS (from main) + static string version(); + static string argString(int argc, char** argv); ///< Return list of arguments as simple string + string allArgsString(); ///< Return all passed arguments as simple string + void parseOpts(FileLine* fl, int argc, char** argv); + void parseOptsList (FileLine* fl, int argc, char** argv); + void parseOptsFile (FileLine* fl, const string& filename); + + // METHODS (generic file utilities) + static string filenameFromDirBase (const string& dir, const string& basename); + static string filenameNonDir (const string& filename); ///< Return non-directory part of filename + static string filenameNonExt (const string& filename); ///< Return non-extensioned (no .) part of filename + static string filenameNonDirExt (const string& filename) { return filenameNonExt(filenameNonDir(filename)); } ///< Return basename of filename + static string filenameDir (const string& filename); ///< Return directory part of filename + static string getenvStr(const char* envvar, const char* defaultValue) { + if (const char* envvalue = getenv(envvar)) { + return envvalue; + } else { + return defaultValue; + } + } + + // METHODS (file utilities using these options) + string fileExists (const string& filename); + string filePath (FileLine* fl, const string& modname, const string& errmsg); + static bool fileStatNormal (const string& filename); +}; + +//###################################################################### + +#endif // guard diff --git a/src/V3Order.cpp b/src/V3Order.cpp new file mode 100644 index 000000000..5badf3706 --- /dev/null +++ b/src/V3Order.cpp @@ -0,0 +1,1559 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Block code ordering +// +// 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. +// +//************************************************************************* +// V3Order's Transformations: +// +// Compute near optimal scheduling of always/wire statements +// Make a graph of the entire netlist +// +// Add master "*INPUTS*" vertex. +// For inputs on top level +// Add vertex for each input var. +// Add edge INPUTS->var_vertex +// +// For seq logic +// Add logic_sensitive_vertex for this list of SenItems +// Add edge for each sensitive_var->logic_sensitive_vertex +// For AssignPre's +// Add vertex for this logic +// Add edge logic_sensitive_vertex->logic_vertex +// Add edge logic_consumed_var_PREVAR->logic_vertex +// Add edge logic_vertex->logic_generated_var (same as if comb) +// Add edge logic_vertex->generated_var_PREORDER +// Cutable dependency to attempt to order dlyed +// assignments to avoid saving state, thus we prefer +// a <= b ... As the opposite order would +// b <= c ... require the old value of b. +// For Logic +// Add vertex for this logic +// Add edge logic_sensitive_vertex->logic_vertex +// Add edge logic_generated_var_PREORDER->logic_vertex +// This insures the AssignPre gets scheduled before this logic +// Add edge logic_vertex->consumed_var_PREVAR +// Add edge logic_vertex->consumed_var_POSTVAR +// Add edge logic_vertex->logic_generated_var (same as if comb) +// For AssignPost's +// Add vertex for this logic +// Add edge logic_sensitive_vertex->logic_vertex +// Add edge logic_consumed_var->logic_vertex (same as if comb) +// Add edge logic_vertex->logic_generated_var (same as if comb) +// +// For comb logic +// For comb logic +// Add vertex for this logic +// Add edge logic_consumed_var->logic_vertex +// Add edge logic_vertex->logic_generated_var +// Mark it cutable, as circular logic may require +// the generated signal to become a primary input again. +// +// +#ifdef NEW_ORDERING +// Determine nodes that form loops within combo logic +// (Strongly connected ignoring assign post/pre's) +// Make a subgraph for each loop of combo logic +// Break circular logic in each subgraph +// +// Determine nodes that form loops within a single eval call +// Make a subgraph for each loop of sequential logic +// Break circular logic in each subgraph +#endif +// +// Rank the graph starting at INPUTS (see V3Graph) +// +// Visit the graph's logic vertices in ranked order +// For all logic vertices with all inputs already ordered +// Make ordered block for this module +// For all ^^ in same domain +// Move logic to ordered activation +// When we have no more choices, we move to the next module +// and make a new block. Add that new activation block to the list of calls to make. +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3File.h" +#include "V3Ast.h" +#include "V3Graph.h" +#include "V3List.h" +#include "V3SenTree.h" +#include "V3Stats.h" + +#include "V3Order.h" +#include "V3OrderGraph.h" + +class OrderMoveDomScope; + +//###################################################################### +// Functions for above graph classes + +void OrderGraph::loopsVertexCb(V3GraphVertex* vertexp) { + if (debug()) cout<<"-Info-Loop: "<(vertexp)) { + cout<nodep()->fileline()<<" "<nodep()->typeName()<(vertexp)) { + cout<varScp()->fileline()<<" "<varScp()->prettyName()< m_readyDomScopeE;// List of next ready dom scope + V3List m_readyVertices; // Ready vertices with same domain & scope +private: + bool m_onReadyList; // True if DomScope is already on list of ready dom/scopes + AstSenTree* m_domainp; // Domain all vertices belong to + AstScope* m_scopep; // Scope all vertices belong to + OrderLoopId m_inLoop; // Loop member of + + typedef pair, AstScope*> DomScopeKey; + typedef std::map DomScopeMap; + static DomScopeMap s_dsMap; // Structure registered for each dom/scope pairing + +public: + OrderMoveDomScope(OrderLoopId inLoop, AstSenTree* domainp, AstScope* scopep) + : m_onReadyList(false), m_domainp(domainp), m_inLoop(inLoop) {} + OrderMoveDomScope* readyDomScopeNextp() const { return m_readyDomScopeE.nextp(); } + OrderLoopId inLoop() const { return m_inLoop; } + AstSenTree* domainp() const { return m_domainp; } + AstScope* scopep() const { return m_scopep; } + void ready(OrderVisitor* ovp); // Check the domScope is on ready list, add if not + void movedVertex(OrderVisitor* ovp, OrderMoveVertex* vertexp); // Mark one vertex as finished, remove from ready list if done + // STATIC MEMBERS (for lookup) + static void clear() { + s_dsMap.clear(); + } + V3List& readyVertices() { return m_readyVertices; } + static OrderMoveDomScope* findCreate (OrderLoopId inLoop, AstSenTree* domainp, AstScope* scopep) { + const DomScopeKey key = make_pair(make_pair(inLoop,domainp),scopep); + DomScopeMap::iterator iter = s_dsMap.find(key); + if (iter != s_dsMap.end()) { + return iter->second; + } else { + OrderMoveDomScope* domScopep = new OrderMoveDomScope(inLoop, domainp, scopep); + s_dsMap.insert(make_pair(key, domScopep)); + return domScopep; + } + } + string name() const { + return (string("MDS:") + +" lp="+cvtToStr(inLoop()) + +" d="+cvtToStr((void*)domainp()) + +" s="+cvtToStr((void*)scopep())); + } +}; + +OrderMoveDomScope::DomScopeMap OrderMoveDomScope::s_dsMap; + +inline ostream& operator<< (ostream& lhs, const OrderMoveDomScope& rhs) { + lhs<=WV_MAX) varscp->v3fatalSrc("Bad Case\n"); + OrderVarVertex* vertexp = m_vertexp[type]; + if (!vertexp) { + UINFO(6,"New vertex "<v3fatalSrc("Bad Case\n"); + } + m_vertexp[type] = vertexp; + } else { + if (createdp) *createdp=false; + } + return vertexp; + } + +public: + // CONSTRUCTORS + OrderUser() { + for (int i=0; i OrderUser* for usage var + // AstActive::user3() -> uint clocks hash of sensitivity list + // {statement}Node::userp-> AstModule* statement is under + // USER5 Cleared on each Logic stmt + // AstVarScope::user5() -> VarUsage(gen/con/both). Where already encountered signal + // Ordering (user3/4/5 cleared between forming and ordering) + // AstScope::userp() -> AstModule*. Module this scope is under + // AstModule::user3() -> Number of routines created + + //int debug() { return 9; } + + // STATE + OrderGraph m_graph; // Scoreboard of var usages/dependencies + SenTreeFinder m_finder; // Find global sentree's and add them + AstSenTree* m_comboDomainp; // Combo activation tree + AstSenTree* m_deleteDomainp;// Delete this from tree + AstSenTree* m_settleDomainp;// Initial activation tree + OrderInputsVertex* m_inputsVxp; // Top level vertex all inputs point from + OrderSettleVertex* m_settleVxp; // Top level vertex all inputs point from + OrderLogicVertex* m_logicVxp; // Current statement being tracked, NULL=ignored + AstTopScope* m_topScopep; // Current top scope being processed + AstScope* m_scopetopp; // Scope under TOPSCOPE + AstModule* m_modp; // Current module + AstScope* m_scopep; // Current scope being processed + AstActive* m_activep; // Current activation block + bool m_inSenTree; // Underneath AstSenItem; any varrefs are clocks + bool m_inClocked; // Underneath clocked block + bool m_inPre; // Underneath AstAssignPre + bool m_inPost; // Underneath AstAssignPost + OrderLogicVertex* m_activeSenVxp; // Sensitivity vertex + deque m_orderUserps; // All created OrderUser's for later deletion. + // STATE... for inside process + OrderLoopId m_loopIdMax; // Maximum BeginLoop id number assigned + vector m_pmlLoopEndps; // processInsLoop: End vertex for each color + vector m_pomLoopMoveps;// processMoveLoop: Loops next nodes are under + AstCFunc* m_pomNewFuncp; // Current function being created + V3Graph m_pomGraph; // Graph of logic elements to move + V3List m_pomWaiting; // List of nodes needing inputs to become ready +protected: + friend class OrderMoveDomScope; + V3List m_pomReadyDomScope; // List of ready domain/scope pairs, by loopId + +private: + // STATS + V3Double0 m_statCut[OrderVEdgeType::_ENUM_END]; // Count of each edge type cut + + // TYPES + enum VarUsage { VU_NONE=0, VU_CON=1, VU_GEN=2 }; + + // METHODS + void iterateNewStmt(AstNode* nodep) { + if (m_scopep) { + UINFO(4," STMT "<sensesp()) nodep->v3fatalSrc("NULL"); + // If inside combo logic, ignore the domain, we'll assign one based on interconnect + AstSenTree* startDomainp = m_activep->sensesp(); + if (startDomainp->hasCombo()) startDomainp=NULL; + m_logicVxp = new OrderLogicVertex(&m_graph, m_scopep, startDomainp, nodep); + if (m_activeSenVxp) { + // If in a clocked activation, add a link from the sensitivity to this block + // Add edge logic_sensitive_vertex->logic_vertex + new OrderEdge(&m_graph, m_activeSenVxp, m_logicVxp, WEIGHT_NORMAL); + } + nodep->userp(m_modp); + nodep->iterateChildren(*this); + m_logicVxp = NULL; + } + } + + OrderVarVertex* newVarUserVertex(AstVarScope* varscp, WhichVertex type, bool* createdp=NULL) { + if (!varscp->userp()) { + OrderUser* newup = new OrderUser(); + m_orderUserps.push_back(newup); + varscp->userp(newup); + } + OrderUser* up = (OrderUser*)(varscp->userp()); + return up->newVarUserVertex(&m_graph, m_scopep, varscp, type, createdp); + } + + V3GraphEdge* findEndEdge(V3GraphVertex* vertexp, AstNode* errnodep, OrderLoopEndVertex*& evertexpr) { + // Given a vertex, find the end block corresponding to it + // Every vertex should have a pointer to the end block (one hopes) + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { + if (OrderLoopEndVertex* evertexp = dynamic_cast(edgep->top())) { + evertexpr = evertexp; + return edgep; + } + } + errnodep->v3fatalSrc("Loop-broken vertex doesn't have pointer to LoopEndVertex: "<user3(1+modp->user3()); + int funcnum = modp->user3(); + string name = (domainp->hasCombo() ? "_combo" + : (domainp->hasInitial() ? "_initial" + : (domainp->hasSettle() ? "_settle" + : (domainp->isMulti() ? "_multiclk" : "_sequent")))); + name = name+"__"+scopep->nameDotless()+"__"+cvtToStr(funcnum); + if (v3Global.opt.profileCFuncs()) { + name += "__PROF__"+forWhatp->fileline()->profileFuncname(); + } + return name; + } + + void nodeMarkCircular(OrderVarVertex* vertexp, OrderEdge* edgep) { + AstVarScope* nodep = vertexp->varScp(); + nodep->circular(true); + m_statCut[vertexp->type()]++; + if (edgep) m_statCut[edgep->type()]++; + if (vertexp->isClock()) { + // Seems obvious; no warning yet + //nodep->v3warn(GENCLK,"Signal unoptimizable: Generated clock: "<prettyName()); + } else if (nodep->varp()->isSigPublic()) { + nodep->v3warn(UNOPT,"Signal unoptimizable: Feedback to public clock or circular logic: "<prettyName()); + } else { + // We don't use UNOPT, as there are lots of V2 places where it was needed, that aren't any more + // First v3warn not inside warnIsOff so we can see the suppressions with --debug + nodep->v3warn(UNOPTFLAT,"Signal unoptimizable: Feedback to clock or circular logic: "<prettyName()); + if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPTFLAT)) { + nodep->fileline()->warnOff(V3ErrorCode::UNOPTFLAT, true); // Complain just once + // Give the user an example. + bool tempWeight = (edgep && edgep->weight()==0); + if (tempWeight) edgep->weight(1); // Else the below loop detect can't see the loop + m_graph.reportLoops(&OrderEdge::followComboConnected, vertexp); // calls OrderGraph::loopsVertexCb + if (tempWeight) edgep->weight(0); + } + } + } + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + nodep->iterateChildren(*this); + // We're finished, complete any unfinished topscopes + if (m_topScopep) { process(); m_topScopep=NULL; } + } + virtual void visit(AstTopScope* nodep, AstNUser*) { + // Process the last thing we're finishing + if (m_topScopep) { process(); m_topScopep=NULL; } + UINFO(2," Loading tree...\n"); + //VV***** We reset userp() + AstNode::userClearTree(); + AstNode::user3ClearTree(); + m_graph.clear(); + m_activep = NULL; + m_topScopep = nodep; + m_scopetopp = nodep->scopep()->castScope(); + // Find sentree's + m_finder.main(m_topScopep); + // ProcessDomainsIterate will use these when it needs to move + // something to a combodomain. This saves a ton of find() operations. + AstSenTree comb (nodep->fileline(), // Gets cloned() so ok if goes out of scope + new AstSenItem(nodep->fileline(), AstSenItem::Combo())); + m_comboDomainp = m_finder.getSenTree(nodep->fileline(), &comb); + AstSenTree settle (nodep->fileline(), // Gets cloned() so ok if goes out of scope + new AstSenItem(nodep->fileline(), AstSenItem::Settle())); + m_settleDomainp = m_finder.getSenTree(nodep->fileline(), &settle); + // Fake AstSenTree we set domainp to indicate needs deletion + m_deleteDomainp = new AstSenTree (nodep->fileline(), + new AstSenItem(nodep->fileline(), AstSenItem::Settle())); + UINFO(5," DeleteDomain = "<iterateChildren(*this); + // Done topscope, erase extra user information + // userp passed to next process() operation + AstNode::user3ClearTree(); + AstNode::user5ClearTree(); + } + virtual void visit(AstModule* nodep, AstNUser*) { + m_modp = nodep; + nodep->iterateChildren(*this); + } + virtual void visit(AstScope* nodep, AstNUser*) { + UINFO(4," SCOPE "<userp(m_modp); + // Iterate + nodep->iterateChildren(*this); + m_scopep = NULL; + } + virtual void visit(AstActive* nodep, AstNUser*) { + // Create required activation blocks and add to module + if (nodep->hasInitial()) return; // Ignore initials + UINFO(4," ACTIVE "<hasClocked(); + // Compute hash of sensitivity list + V3Hash clocks; + if (nodep->sensesp()) { + for (AstSenItem* senp = nodep->sensesp()->sensesp(); senp; senp=senp->nextp()->castSenItem()) { + clocks = V3Hash(clocks, V3Hash(senp->edgeType())); + if (senp->varrefp()) { + clocks = V3Hash(clocks, V3Hash(senp->varrefp()->varScopep())); + } + } + } + nodep->user3(clocks.hshval()); + // Grab the sensitivity list + if (nodep->sensesStorep()) nodep->v3fatalSrc("Senses should have been activeTop'ed to be global!"); + nodep->sensesp()->accept(*this); + // Collect statements under it + nodep->iterateChildren(*this); + m_activep = NULL; + } + virtual void visit(AstVarScope* nodep, AstNUser*) { + // Create links to all input signals + if (m_modp->isTop() && nodep->varp()->isInput()) { + OrderVarVertex* varVxp = newVarUserVertex(nodep, WV_STD); + new OrderEdge(&m_graph, m_inputsVxp, varVxp, WEIGHT_INPUT); + } + } + virtual void visit(AstNodeVarRef* nodep, AstNUser*) { + if (m_scopep) { + AstVarScope* varscp = nodep->varScopep(); + if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp\n"); + if (m_inSenTree) { + // Add CLOCK dependency... This is a root of the tree we'll trace + if (nodep->lvalue()) nodep->v3fatalSrc("How can a sensitivity be setting a var?\n"); + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); + varVxp->isClock(true); + new OrderEdge(&m_graph, varVxp, m_activeSenVxp, WEIGHT_MEDIUM); + } else { + if (!m_logicVxp) nodep->v3fatalSrc("Var ref not under a logic block\n"); + // What new directions is this used + // We don't want to add extra edges if the logic block has many usages of same var + bool gen = false; + bool con = false; + if (nodep->lvalue()) { + gen = !(varscp->user5() & VU_GEN); + } else { + con = !(varscp->user5() & VU_CON); + if ((varscp->user5() & VU_GEN) && !m_inClocked) { + // Dangerous assumption: + // If a variable is used in the same activation which defines it first, + // consider it something like: + // foo = 1 + // foo = foo + 1 + // and still optimize. This is the rule verilog-mode assumes for /*AS*/ + // Note this will break though: + // if (sometimes) foo = 1 + // foo = foo + 1 + con = false; + } + if (varscp->varp()->attrClockEn() && !m_inPre && !m_inPost && !m_inClocked) { + // clock_enable attribute: user's worring about it for us + con = false; + } + } + if (gen) varscp->user5(varscp->user5() | VU_GEN); + if (con) varscp->user5(varscp->user5() | VU_CON); + // Add edges + if (!m_inClocked + || m_inPost + ) { + // Combo logic + if ( +#ifdef NEW_ORDERING + m_activep && m_activep->hasSettle() +#else + 0 +#endif + ) { + // Inside settlement; we use special variable names to prevent + // having extra logic to break arcs within + if (gen) { + // Add edge logic_vertex->logic_generated_var + bool created = false; + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_SETL, &created); + new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp); +#ifdef NEW_ORDERING + if (created) new OrderEdge(&m_graph, m_settleVxp, varVxp, WEIGHT_INPUT); +#endif + } + if (con) { + // Add edge logic_consumed_var->logic_vertex + bool created = false; + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_SETL, &created); + new OrderEdge(&m_graph, varVxp, m_logicVxp, WEIGHT_MEDIUM); +#ifdef NEW_ORDERING + if (created) new OrderEdge(&m_graph, m_settleVxp, varVxp, WEIGHT_INPUT); +#endif + } + } else { // not settle and (combo or inPost) + if (gen) { + // Add edge logic_vertex->logic_generated_var + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); + if (m_inPost) { + new OrderPostCutEdge(&m_graph, m_logicVxp, varVxp); + } else { + new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp); + } + // For m_inPost: + // Add edge consumed_var_POST->logic_vertex + // This prevents a consumer of the "early" value to be scheduled + // after we've changed to the next-cycle value + // ALWAYS do it: + // There maybe a wire a=b; between the two blocks + OrderVarVertex* postVxp = newVarUserVertex(varscp, WV_POST); + new OrderEdge(&m_graph, postVxp, m_logicVxp, WEIGHT_POST); + } + if (con) { + // Add edge logic_consumed_var->logic_vertex + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); + new OrderEdge(&m_graph, varVxp, m_logicVxp, WEIGHT_MEDIUM); + } + } + } else if (m_inPre) { + // AstAssignPre logic + if (gen) { + // Add edge logic_vertex->generated_var_PREORDER + OrderVarVertex* ordVxp = newVarUserVertex(varscp, WV_PORD); + new OrderEdge(&m_graph, m_logicVxp, ordVxp, WEIGHT_NORMAL); + // Add edge logic_vertex->logic_generated_var (same as if comb) + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); + new OrderEdge(&m_graph, m_logicVxp, varVxp, WEIGHT_NORMAL); + } + if (con) { + // Add edge logic_consumed_var_PREVAR->logic_vertex + // This one is cutable (vs the producer) as there's only one of these, but many producers + OrderVarVertex* preVxp = newVarUserVertex(varscp, WV_PRE); + new OrderPreCutEdge(&m_graph, preVxp, m_logicVxp); + } + } else { + // Seq logic + if (gen) { + // Add edge logic_generated_var_PREORDER->logic_vertex + OrderVarVertex* ordVxp = newVarUserVertex(varscp, WV_PORD); + new OrderEdge(&m_graph, ordVxp, m_logicVxp, WEIGHT_NORMAL); + // Add edge logic_vertex->logic_generated_var (same as if comb) + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); + new OrderEdge(&m_graph, m_logicVxp, varVxp, WEIGHT_NORMAL); + } + if (con) { + // Add edge logic_vertex->consumed_var_PREVAR + // Generation of 'pre' because we want to indicate it should be before AstAssignPre + OrderVarVertex* preVxp = newVarUserVertex(varscp, WV_PRE); + new OrderEdge(&m_graph, m_logicVxp, preVxp, WEIGHT_NORMAL); + // Add edge logic_vertex->consumed_var_POST + OrderVarVertex* postVxp = newVarUserVertex(varscp, WV_POST); + new OrderEdge(&m_graph, m_logicVxp, postVxp, WEIGHT_POST); + } + } + } + } + } + virtual void visit(AstSenTree* nodep, AstNUser*) { + // Having a node derived from the sentree isn't required for + // correctness, it mearly makes the graph better connected + // and improves graph algorithmic performance + if (m_scopep) { // Else TOPSCOPE's SENTREE list + m_inSenTree = true; + if (nodep->hasClocked()) { + if (!m_activeSenVxp) { + m_activeSenVxp = new OrderLogicVertex(&m_graph, m_scopep, nodep, m_activep); + } + nodep->iterateChildren(*this); + } + m_inSenTree = false; + } + } + virtual void visit(AstAlways* nodep, AstNUser*) { + iterateNewStmt(nodep); + } + virtual void visit(AstAlwaysPost* nodep, AstNUser*) { + m_inPost = true; + iterateNewStmt(nodep); + m_inPost = false; + } + virtual void visit(AstAssignAlias* nodep, AstNUser*) { + iterateNewStmt(nodep); + } + virtual void visit(AstAssignW* nodep, AstNUser*) { + iterateNewStmt(nodep); + } + virtual void visit(AstAssignPre* nodep, AstNUser*) { + m_inPre = true; + iterateNewStmt(nodep); + m_inPre = false; + } + virtual void visit(AstAssignPost* nodep, AstNUser*) { + m_inPost = true; + iterateNewStmt(nodep); + m_inPost = false; + } + virtual void visit(AstCFunc*, AstNUser*) { + // Ignore for now + // We should detect what variables are set in the function, and make + // settlement code for them, then set a global flag, so we call "settle" + // on the next evaluation loop. + } + //-------------------- + // Default + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + OrderVisitor() { + m_topScopep = NULL; + m_scopetopp = NULL; + m_modp = NULL; + m_scopep = NULL; + m_activep = NULL; + m_inSenTree = false; + m_inClocked = false; + m_inPre = m_inPost = false; + m_comboDomainp = NULL; + m_deleteDomainp = NULL; + m_settleDomainp = NULL; + m_settleVxp = NULL; + m_inputsVxp = NULL; + m_loopIdMax = LOOPID_FIRST; + if (debug()) m_graph.debug(5); // 3 is default if global debug; we want acyc debugging + } + virtual ~OrderVisitor() { + // Stats + for (int type=0; type::iterator it=m_orderUserps.begin(); it!=m_orderUserps.end(); ++it) { + delete *it; + } + m_graph.debug(V3Error::debugDefault()); + } + void main(AstNode* nodep) { + nodep->accept(*this); + } +}; + +//###################################################################### +// Pre-Loop elimination + +void OrderVisitor::processInsLoop() { + // Input is graph with color() reflecting the sub-graphs we need + // to loop remove. Take all the I/O from each subgraph and route + // through a new begin/end vertex + // Note we DON'T only do certain edges; we want all edges to be preserved, + // we'll select which are important later on. + + uint32_t maxcolor = 0; + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { + if (maxcolor <= vertexp->color()) maxcolor = vertexp->color() + 1; + if (OrderVarVertex* vvertexp = dynamic_cast(vertexp)) { + vvertexp->pilNewVertexp(NULL); // Clear user-ish information before loop + } + } + + m_pmlLoopEndps.clear(); + m_pmlLoopEndps.reserve(maxcolor); + m_pmlLoopEndps.assign(maxcolor,NULL); + + m_graph.userClearVertices(); // Vertex::user() // true if added as begin/end + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { + if (uint32_t loopColor = vertexp->color()) { + OrderEitherVertex* evertexp = dynamic_cast(vertexp); + if (!evertexp) v3fatalSrc("Non either vertex found"); + if (!vertexp->user()) { + UINFO(8," pil:nowInLoop lp="<fileline(), NULL, NULL); + m_topScopep->addStmtsp(untilp); + OrderLoopBeginVertex* beginp + = new OrderLoopBeginVertex(&m_graph, m_scopetopp, + evertexp->domainp(), + untilp, + m_loopIdMax, loopColor); + m_loopIdMax = (OrderLoopId)(m_loopIdMax+1); + UASSERT(LOOPID_MAX>m_loopIdMax, "loopid overflow "<color(loopColor); + endp->color(loopColor); + beginp->user(1); // Added; don't iterate on it + endp->user(1); // Added; don't iterate on it + } + if (evertexp->inLoop()) { + // Adding node to loop, but already in loop... + // Ok if the nodes were a combo loop and now become a sequent loop, + // The combo loop will be "under" the sequent loop, so keep the combo #. + //UINFO(9, "Adding node to loop, but already in loop: "<name()<beginVertexp(); + new OrderEdge(&m_graph, beginp, vertexp, WEIGHT_LOOPBE); + new OrderEdge(&m_graph, vertexp, endp, WEIGHT_LOOPBE); + evertexp->inLoop(beginp->loopId()); + } + } + } + } + + m_graph.userClearEdges(); // Edge::user() // true if we added it + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { + if (vertexp->color()) { + for (V3GraphEdge* nextp,* edgep = vertexp->outBeginp(); edgep; edgep = nextp) { + nextp = edgep->outNextp(); // Func may edit the list + if (edgep->weight()) { + processInsLoopEdge(edgep); + } else { // No purpose to this edge any longer + edgep->unlinkDelete(); edgep=NULL; // remove old edge + } + } + for (V3GraphEdge* nextp,* edgep = vertexp->inBeginp(); edgep; edgep = nextp) { + nextp = edgep->inNextp(); // Func may edit the list + if (edgep->weight()) { + processInsLoopEdge(edgep); + } else { // No purpose to this edge any longer + edgep->unlinkDelete(); edgep=NULL; // remove old edge + } + } + } + } + + // Done with space + m_pmlLoopEndps.clear(); +} + +void OrderVisitor::processInsLoopNewEdge(V3GraphEdge* oldEdgep, V3GraphVertex* newFromp, V3GraphVertex* newTop) { + // Create new edge based on the old edge's parameters + const OrderEdge* oedgep = dynamic_cast(oldEdgep); + V3GraphEdge* newEdgep = oedgep->clone(&m_graph, newFromp, newTop); + newEdgep->user(1); // New edge doesn't need to be changed +} + +OrderVarVertex* OrderVisitor::processInsLoopNewVar(OrderVarVertex* oldVertexp, bool& createdr) { + // Create new VarVertex from given template + if (oldVertexp->pilNewVertexp()) { + createdr = false; + } else { + OrderVarVertex* newVertexp = oldVertexp->clone(&m_graph); + oldVertexp->pilNewVertexp(newVertexp); + createdr = true; + } + return oldVertexp->pilNewVertexp(); +} + +void OrderVisitor::processInsLoopEdge(V3GraphEdge* oldEdgep) { + if (!oldEdgep->user()) { // if not processed + oldEdgep->user(1); // mark as processed + if (oldEdgep->fromp()->color() == oldEdgep->top()->color()) { + return; // Doesn't cross subgraphs + } else { + V3GraphVertex* newFromp = oldEdgep->fromp(); + V3GraphVertex* newTop = oldEdgep->top(); + //UINFO(6, " pile: "< "<fromp()->color()) { + // Change to come from end block + OrderLoopEndVertex* endp = m_pmlLoopEndps[fromId]; + newFromp = endp; + // If it goes from(to) a cutable VarVertex inside the Begin/End block, + // we can't loose the variable, as we might need to cut that variable out + // in the next pass of processLoops, and processBrokeLoops needs the var pointer. + // We'll make another VarVertex (dup of the one "inside" the loop) + // and point to it. + if (oldEdgep->cutable()) { + if (OrderVarVertex* vvFromp = dynamic_cast(oldEdgep->fromp())) { + // end => newvarvtx -> {whatever} + bool created; + newFromp = processInsLoopNewVar(vvFromp, created/*ref*/); + if (created) processInsLoopNewEdge(oldEdgep, endp, newFromp); + } + } + } + if (uint32_t toId = oldEdgep->top()->color()) { + // Change to go to begin block + OrderLoopEndVertex* endp = m_pmlLoopEndps[toId]; + OrderLoopBeginVertex* beginp = endp->beginVertexp(); + newTop = beginp; + // Ditto above + if (oldEdgep->cutable()) { + if (OrderVarVertex* vvTop = dynamic_cast(oldEdgep->top())) { + // oldfrom -> newvarvtx => begin + bool created; + newTop = processInsLoopNewVar(vvTop, created/*ref*/); + if (created) processInsLoopNewEdge(oldEdgep, newTop, beginp); + } + } + } + + // New edge with appropriate to/from + processInsLoopNewEdge(oldEdgep, newFromp, newTop); + oldEdgep->unlinkDelete(); oldEdgep=NULL; // remove old edge + } + } +} + +//###################################################################### +// Pre-Loop elimination + +// NEW_ORDERING +void OrderVisitor::processBrokeLoop() { + // Find those loops that were broken + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { + // Above processInsLoopEdge makes sure there's always a OrderVarVertex on + // boundaries to/from LoopBegin/EndVertex'es + if (OrderVarVertex* vvertexp = dynamic_cast(vertexp)) { + // Any cut edges? + bool anyCut = false; + for (V3GraphEdge* nextp,* edgep = vertexp->inBeginp(); edgep; edgep = nextp) { + nextp = edgep->inNextp(); // We may edit the list + if (edgep->weight()==0) { // was cut + anyCut = true; + edgep->unlinkDelete(); edgep=NULL; // remove old edge + } + } + for (V3GraphEdge* nextp,* edgep = vertexp->outBeginp(); edgep; edgep = nextp) { + nextp = edgep->outNextp(); // We may edit the list + if (edgep->weight()==0) { // was cut + anyCut = true; + edgep->unlinkDelete(); edgep=NULL; // remove old edge + } + } + if (anyCut) { + UINFO(6," pbl: Cut "<name()<varScp(), endp/*ref*/); + // Add edge to graphically indicate change detect required + endedgep->unlinkDelete(); endedgep=NULL; // remove old edge + new OrderChangeDetEdge(&m_graph, vvertexp, endp); + // Add variable dependency to until loop + AstUntilStable* untilp = endp->beginVertexp()->untilp(); + untilp->addStablesp(new AstVarRef(vvertexp->varScp()->fileline(), vvertexp->varScp(), false)); + } + } + } +} + +//###################################################################### +// Circular detection + +#ifndef NEW_ORDERING +void OrderVisitor::processCircular() { + // Take broken edges and add circular flags + // The change detect code will use this to force changedets + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (OrderVarStdVertex* vvertexp = dynamic_cast(itp)) { + if (vvertexp->isClock() && !vvertexp->varScp()->varp()->isPrimaryIn()) { + // If a clock is generated internally, we need to do another loop + // through the entire evaluation. This fixes races; see t_clk_dpulse test. + UINFO(5,"Circular Clock "<outBeginp(); edgep; edgep=edgep->outNextp()) { + if (edgep->weight()==0) { // was cut + OrderEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) vvertexp->varScp()->v3fatalSrc("Cuttable edge not of proper type"); + nodeMarkCircular(vvertexp, oedgep); + } + } + for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + if (edgep->weight()==0) { // was cut + OrderEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) vvertexp->varScp()->v3fatalSrc("Cuttable edge not of proper type"); + nodeMarkCircular(vvertexp, oedgep); + } + } + } + } +} +#endif + +void OrderVisitor::processSensitive() { + // Sc sensitives are required on all inputs that go to a combo + // block. (Not inputs that go only to clocked blocks.) + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (OrderVarStdVertex* vvertexp = dynamic_cast(itp)) { + if (vvertexp->varScp()->varp()->isInput()) { + //UINFO(0," scsen "<outBeginp(); edgep; edgep=edgep->outNextp()) { + if (OrderEitherVertex* toVertexp = dynamic_cast(edgep->top())) { + if (edgep->weight() && toVertexp->domainp()) { + //UINFO(0," "<domainp()<domainp()->hasCombo()) { + vvertexp->varScp()->varp()->scSensitive(true); + } + } + } + } + } + } + } +} + +void OrderVisitor::processDomains() { + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + OrderEitherVertex* vertexp = dynamic_cast(itp); + UASSERT(vertexp, "Null or vertex not derived from EitherVertex\n"); + processDomainsIterate(vertexp); + } +} + +void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) { + // The graph routines have already sorted the vertexes and edges into best->worst order + // Assign clock domains to each signal. + // Sequential logic is forced into the same sequential domain. + // Combo logic may be pushed into a seq domain if all its inputs are the same domain, + // else, if all inputs are from flops, it's end-of-sequential code + // else, it's full combo code + if (!vertexp->inLoop()) vertexp->inLoop(LOOPID_NOTLOOPED); + if (vertexp->domainp()) return; // Already processed, or sequential logic + UINFO(5," pdi: "<(vertexp); + AstSenTree* domainp = NULL; + UASSERT(m_comboDomainp, "not preset"); +#ifndef NEW_ORDERING // New ordering set the input node as combo, so this happens automatically + if (vvertexp && vvertexp->varScp()->varp()->isInput()) { + domainp = m_comboDomainp; + } +#endif + if (vvertexp && vvertexp->varScp()->isCircular()) { + domainp = m_comboDomainp; + } + if (!domainp) { + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + OrderEitherVertex* fromVertexp = (OrderEitherVertex*)edgep->fromp(); + if (edgep->weight() +#ifndef NEW_ORDERING + && fromVertexp->domainMatters() +#endif + ) { + UINFO(9," from d="<<(void*)fromVertexp->domainp()<<" "<hasSettle()) { // or, we can ignore being in the settle domain + domainp = fromVertexp->domainp(); + } + else if (domainp->hasCombo()) { + // Once in combo, keep in combo; already as severe as we can get + } + else if (fromVertexp->domainp()->hasCombo()) { + // Any combo input means this vertex must remain combo + domainp = m_comboDomainp; + } + else if (fromVertexp->domainp()->hasSettle()) { + // Ignore that we have a constant (initial) input + } + else if (domainp != fromVertexp->domainp()) { + // Make a domain that merges the two domains + bool ddebug = debug()>=9; + if (ddebug) { + cout<addSensesp(newtree2p); + newtreep->sortSenses(); // Remove duplicates + newtreep->multi(true); // Comment that it was made from 2 clock domains + domainp = m_finder.getSenTree(domainp->fileline(), newtreep); + if (ddebug) { + UINFO(0," dnew ="<dumpTree(cout); + UINFO(0," find ="<dumpTree(cout); + cout<deleteTree(); newtreep=NULL; + } + } + } // next input edgep + // Default the domain + // This is a node which has only constant inputs, or is otherwise indeterminate. + // It should have already been copied into the settle domain. Presumably it has + // inputs which we never trigger, or nothing it's sensitive to, so we can rip it out. + if (!domainp && vertexp->scopep()) { + domainp = m_deleteDomainp; + } + } + // + vertexp->domainp(domainp); + if (vertexp->domainp()) { + UINFO(5," done d="<<(void*)vertexp->domainp() + <<(vertexp->domainp()->hasCombo()?" [COMB]":"") + <<(vertexp->domainp()->isMulti()?" [MULT]":"") + <<" "< logp (V3File::new_ofstream(filename)); + if (logp->fail()) v3fatalSrc("Can't write "< report; + + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (OrderVarVertex* vvertexp = dynamic_cast(itp)) { + string name (vvertexp->varScp()->prettyName()); + if (dynamic_cast(itp)) name += " {PRE}"; + else if (dynamic_cast(itp)) name += " {POST}"; + else if (dynamic_cast(itp)) name += " {PORD}"; + else if (dynamic_cast(itp)) name += " {STL}"; + ostringstream os; + os.setf(ios::left); + os<<" "<domainp(); + if (sentreep) { + bool first = true; + for (AstSenItem* itemp=sentreep->sensesp(); itemp; itemp=itemp->nextp()->castSenItem()) { + if (first) os<<"@( "; else os<<", "; + first = false; + os<edgeType().verilogKwd(); + if (itemp->varrefp()) { + os<<" "+itemp->varrefp()->prettyName(); + } + } + if (!first) os<<" )"; + } + report.push_back(os.str()); + } + } + + *logp<<"Signals and their clock domains:"<::iterator it=report.begin(); it!=report.end(); ++it) { + *logp<<(*it)<verticesNextp()) { + if (OrderLogicVertex* lvertexp = dynamic_cast(itp)) { + OrderMoveVertex* moveVxp = new OrderMoveVertex(&m_pomGraph, lvertexp); + moveVxp->m_pomWaitingE.pushBack(m_pomWaiting, moveVxp); + // Cross link so we can find it later + lvertexp->moveVxp(moveVxp); + } + } + // Build edges between logic vertices + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (OrderLogicVertex* lvertexp = dynamic_cast(itp)) { + OrderMoveVertex* moveVxp = lvertexp->moveVxp(); + processMoveBuildGraphIterate(moveVxp, lvertexp, 0); + } + } +} + +void OrderVisitor::processMoveBuildGraphIterate (OrderMoveVertex* moveVxp, V3GraphVertex* vertexp, int weightmin) { + // Search forward from given logic vertex, making new edges based on moveVxp + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (edgep->weight()!=0) { // was cut + int weight = weightmin; + if (weight==0 || weight>edgep->weight()) weight=edgep->weight(); + if (OrderLogicVertex* toLVertexp = dynamic_cast(edgep->top())) { + // Path from vertexp to a logic vertex; new edge + // Note we use the last edge's weight, not some function of multiple edges + new OrderEdge(&m_pomGraph, moveVxp, toLVertexp->moveVxp(), weight); + } + else { // Keep hunting forward for a logic node + processMoveBuildGraphIterate(moveVxp, edgep->top(), weight); + } + } + } +} + +//###################################################################### +// Moving + +void OrderVisitor::processMove() { + // The graph routines have already sorted the vertexes and edges into best->worst order + // Make a new waiting graph with only OrderLogicVertex's + // (Order is preserved in the recreation so the sorting is preserved) + // Move any node with all inputs ready to a "ready" graph mapped by domain and then scope + // While waiting graph ! empty (and also known: something in ready graph) + // For all scopes in domain of top ready vertex + // For all vertexes in domain&scope of top ready vertex + // Make ordered activation block for this module + // Add that new activation to the list of calls to make. + // Move logic to ordered active + // Any children that have all inputs now ready move from waiting->ready graph + // (This may add nodes the for loop directly above needs to detext) + processMovePrepScopes(); + processMovePrepReady(); + + // New domain... another loop + UINFO(5," MoveIterate\n"); +#ifdef NEW_ORDERING + OrderMoveDomScope* domScopep = NULL; // Currently active domain/scope + while (!m_pomReadyDomScope.empty()) { + // Always need to reamin in same loop construct + OrderLoopId curLoop = processMoveLoopCurrent(); + // Scan list to find search candidates + OrderMoveDomScope* loopHuntp = NULL; // Found domscope under same loop + OrderMoveDomScope* domHuntp = NULL; // Found domscope under same domain + OrderMoveDomScope* scopeHuntp = NULL; // Found domscope under same scope + if (domScopep) { UINFO(6," MoveSearch: loop="<readyDomScopeNextp()) { + if (huntp->inLoop() == curLoop) { + if (!loopHuntp) loopHuntp = huntp; + if (domScopep && huntp->domainp() == domScopep->domainp()) { + if (!domHuntp) domHuntp = huntp; + if (domScopep && huntp->scopep() == domScopep->scopep()) { + if (!scopeHuntp) scopeHuntp = huntp; + break; // Exact match; all we can hope for + } + } + } + } + // Recompute the next domScopep to process + if (scopeHuntp) { + domScopep = scopeHuntp; + UINFO(6," MoveIt: SameScope "<<*domScopep<domainp()->v3fatalSrc("Can't find more nodes like "<<*domScopep); // Should be at least a "end loop" + break; + } + // Work on all vertices in this loop/domain/scope + OrderMoveVertex* topVertexp = domScopep->readyVertices().begin(); + UASSERT(topVertexp, "domScope on ready list without any nodes ready under it"); + m_pomNewFuncp = NULL; + while (OrderMoveVertex* vertexp = domScopep->readyVertices().begin()) { + processMoveOne(vertexp, domScopep, 1); + if (curLoop != processMoveLoopCurrent()) break; // Hit a LoopBegin/end, change loop + } + } + if (!m_pomWaiting.empty()) { + OrderMoveVertex* vertexp = m_pomWaiting.begin(); + vertexp->logicp()->nodep()->v3fatalSrc("Didn't converge; nodes waiting, none ready, perhaps some input activations lost: "<readyVertices().begin(); + UASSERT(topVertexp, "domScope on ready list without any nodes ready under it"); + // Work on all scopes ready inside this domain + while (domScopep) { + UINFO(6," MoveDomain l="<domainp()<readyVertices().begin()) { + processMoveOne(vertexp, domScopep, 1); + } + // Done with scope/domain pair, pick new scope under same domain, or NULL if none left + OrderMoveDomScope* domScopeNextp = NULL; + for (OrderMoveDomScope* huntp = m_pomReadyDomScope.begin(); + huntp; huntp = huntp->readyDomScopeNextp()) { + if (huntp->domainp() == domScopep->domainp()) { + domScopeNextp = huntp; + break; + } + } + domScopep = domScopeNextp; + } + } + UASSERT (m_pomWaiting.empty(), "Didn't converge; nodes waiting, none ready, perhaps some input activations lost."); +#endif + // Cleanup memory + processMoveClear(); +} + +void OrderVisitor::processMovePrepScopes() { + UINFO(5," MovePrepScopes\n"); + // Create a OrderMoveDomScope every domain/scope pairing + for (OrderMoveVertex* vertexp = m_pomWaiting.begin(); vertexp; vertexp=vertexp->pomWaitingNextp()) { + AstSenTree* domainp = vertexp->logicp()->domainp(); + AstScope* scopep = vertexp->logicp()->scopep(); + OrderLoopId inLoop = vertexp->logicp()->inLoop(); + // Create the dom pairing for later lookup + OrderMoveDomScope* domScopep = OrderMoveDomScope::findCreate(inLoop, domainp, scopep); + vertexp->domScopep(domScopep); + } +} + +void OrderVisitor::processMovePrepReady() { + // Make list of ready nodes + UINFO(5," MovePrepReady\n"); + for (OrderMoveVertex* vertexp = m_pomWaiting.begin(); vertexp; ) { + OrderMoveVertex* nextp = vertexp->pomWaitingNextp(); + if (vertexp->isWait() && vertexp->inEmpty()) { + processMoveReadyOne(vertexp); + } + vertexp = nextp; + } +} + +void OrderVisitor::processMoveReadyOne(OrderMoveVertex* vertexp) { + // Recursive! + // Move one node from waiting to ready list + vertexp->setReady(); + // Remove node from waiting list + vertexp->m_pomWaitingE.unlink(m_pomWaiting, vertexp); + // Add to ready list (indexed by domain and scope) + vertexp->m_readyVerticesE.pushBack(vertexp->domScopep()->m_readyVertices, vertexp); + vertexp->domScopep()->ready (this); +} + +void OrderVisitor::processMoveDoneOne(OrderMoveVertex* vertexp) { + // Move one node from ready to completion + vertexp->setMoved(); + // Unlink from ready lists + vertexp->m_readyVerticesE.unlink(vertexp->domScopep()->m_readyVertices, vertexp); + vertexp->domScopep()->movedVertex (this, vertexp); + // Don't need to add it to another list, as we're done with it + // Mark our outputs as one closer to ready + for (V3GraphEdge* edgep = vertexp->outBeginp(), *nextp; edgep; edgep=nextp) { + nextp = edgep->outNextp(); + OrderMoveVertex* toVertexp = (OrderMoveVertex*)edgep->top(); + UINFO(9," Clear to "<<(toVertexp->inEmpty()?"[EMP] ":" ") + <unlinkDelete(); edgep=NULL; + if (toVertexp->inEmpty()) { + // If destination node now has all inputs resolved; recurse to move that vertex + // This is thus depth first (before width) which keeps the resulting executable's d-cache happy. + processMoveReadyOne(toVertexp); + } + } +} + +void OrderVisitor::processMoveOne(OrderMoveVertex* vertexp, OrderMoveDomScope* domScopep, int level) { + UASSERT(vertexp->domScopep() == domScopep, "Domain mismatch; list misbuilt?\n"); + OrderLogicVertex* lvertexp = vertexp->logicp(); + AstScope* scopep = lvertexp->scopep(); + UINFO(5," POSmove l"<domainp(); + AstNode* nodep = lvertexp->nodep(); + AstModule* modp = scopep->userp()->castNode()->castModule(); UASSERT(modp,"NULL"); // Stashed by visitor func + if (nodep->castUntilStable()) { +#ifdef NEW_ORDERING + // Beginning of loop. + if (OrderLoopBeginVertex* beginp = dynamic_cast(lvertexp)) { + m_pomNewFuncp = NULL; // Close out any old function + // Create new active record + string name = cfuncName(modp, domainp, scopep, nodep); + AstActive* callunderp = new AstActive(nodep->fileline(), name, domainp); + if (domainp == m_deleteDomainp) { + UINFO(6," Delete loop "<unlinkFrBack(); + callunderp->addStmtsp(nodep); + // Remember we're in a loop + processMoveLoopPush(beginp); + } + else if (OrderLoopEndVertex* endp = dynamic_cast(lvertexp)) { + // Nodep is identical to OrderLoopBeginVertex's, so we don't move it + m_pomNewFuncp = NULL; // Close out any old function + processMoveLoopPop(endp->beginVertexp()); + } + else { + nodep->v3fatalSrc("AstUntilStable node isn't under a OrderLoop{End}Vertex.\n"); + } +#else + nodep->v3fatalSrc("Not implemented"); +#endif + } + else if (nodep->castSenTree()) { + // Just ignore sensitivities, we'll deal with them when we move statements that need them + } + else { // Normal logic + // Make or borrow a CFunc to contain the new statements + if (v3Global.opt.profileCFuncs()) { + // Put every statement into a unique function to ease profiling + m_pomNewFuncp = NULL; + } + if (!m_pomNewFuncp && domainp != m_deleteDomainp) { + string name = cfuncName(modp, domainp, scopep, nodep); + m_pomNewFuncp = new AstCFunc(nodep->fileline(), name, scopep); + if (domainp->hasInitial() || domainp->hasSettle()) m_pomNewFuncp->slow(true); + scopep->addActivep(m_pomNewFuncp); + // Where will we be adding the call? + AstActive* callunderp = new AstActive(nodep->fileline(), name, domainp); + processMoveLoopStmt(callunderp); + // Add a top call to it + AstCCall* callp = new AstCCall(nodep->fileline(), m_pomNewFuncp); + callunderp->addStmtsp(callp); + UINFO(6," New "<unlinkFrBack(); + if (domainp == m_deleteDomainp) { + UINFO(4," Ordering deleting pre-settled "<addStmtsp(nodep); + } + } + processMoveDoneOne (vertexp); +} + +inline void OrderVisitor::processMoveLoopPush(OrderLoopBeginVertex* beginp) { + UINFO(6," LoopPush "<nodep()->v3fatalSrc("processMoveLoopPop with no push'ed loops"); + OrderLoopBeginVertex* topBeginp = m_pomLoopMoveps.back(); + if (topBeginp != beginp) beginp->nodep()->v3fatalSrc("processMoveLoopPop had different vertex then one expected, got="<m_pomReadyDomScope, this); + } +} + +inline void OrderMoveDomScope::movedVertex(OrderVisitor* ovp, OrderMoveVertex* vertexp) { // Mark one vertex as finished, remove from ready list if done + UASSERT(m_onReadyList, "Moving vertex from ready when nothing was on que as ready."); + if (m_readyVertices.empty()) { // Else more work to get to later + m_onReadyList = false; + m_readyDomScopeE.unlink(ovp->m_pomReadyDomScope, this); + } +} + +//###################################################################### +// Top processing + +void OrderVisitor::processLoops(string stepName, V3EdgeFuncP edgeFuncp) { + UINFO(2," "<=4) m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_loops", true); + + // Remove loops from the graph + UINFO(2," "<=4) m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_broke", true); + + m_graph.makeEdgesNonCutable(edgeFuncp); + m_graph.dumpDotFilePrefixed((string)"orderg_"+stepName+"_done", true); +} + +void OrderVisitor::process() { + // Dump data + m_graph.dumpDotFilePrefixed("orderg_pre"); + +#ifdef NEW_ORDERING + // Ignoring POST assignments and clocked statements, detect strongly connected components + // Split components into subgraphs + + // Detect, loop begin/end, and acyc combo loops + UINFO(2," Combo loop elimination...\n"); + processLoops("combo", &OrderEdge::followComboConnected); + + // Detect, loop begin/end, and acyc post assigns + UINFO(2," Sequential loop elimination...\n"); + processLoops("sequent", &OrderEdge::followSequentConnected); + + // Detect and acyc any PRE assigns + // As breaking these does not cause any problems, we don't need to loop begin/end, etc. + UINFO(2," Pre Loop Detect & Acyc...\n"); + m_graph.stronglyConnected(&V3GraphEdge::followAlwaysTrue); + if (debug()>=4) m_graph.dumpDotFilePrefixed("orderg_preasn_strong", true); + m_graph.acyclic(&V3GraphEdge::followAlwaysTrue); + m_graph.dumpDotFilePrefixed("orderg_preasn_done", true); +#else + // Break cycles + UINFO(2," Acyclic & Order...\n"); + m_graph.acyclic(&V3GraphEdge::followAlwaysTrue); + m_graph.dumpDotFilePrefixed("orderg_acyc"); +#endif + + // Assign ranks so we know what to follow + // Then, sort vertices and edges by that ordering + m_graph.order(); + m_graph.dumpDotFilePrefixed("orderg_order"); + +#ifndef NEW_ORDERING + UINFO(2," Process Circulars...\n"); + processCircular(); // must be before processDomains +#endif + + // Assign logic verticesto new domains + UINFO(2," Domains...\n"); + processDomains(); + m_graph.dumpDotFilePrefixed("orderg_domain"); + + if (debug() && v3Global.opt.dumpTree()) processEdgeReport(); + + UINFO(2," Construct Move Graph...\n"); + processMoveBuildGraph(); + if (debug()>=4) m_pomGraph.dumpDotFilePrefixed("ordermv_start"); // Different prefix (ordermv) as it's not the same graph + m_pomGraph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); + m_pomGraph.dumpDotFilePrefixed("ordermv_simpl"); + + UINFO(2," Move...\n"); + processMove(); + + // Any SC inputs feeding a combo domain must be marked, so we can make them sc_sensitive + UINFO(2," Sensitive...\n"); + processSensitive(); // must be after processDomains + + // Dump data + m_graph.dumpDotFilePrefixed("orderg_done"); + if (0 && debug()) { + string dfilename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_INT_order.tree"; + const auto_ptr logp (V3File::new_ofstream(dfilename)); + if (logp->fail()) v3fatalSrc("Can't write "<(_e)) {}; + operator en () const { return m_e; }; + }; + inline bool operator== (OrderVEdgeType lhs, OrderVEdgeType rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator== (OrderVEdgeType lhs, OrderVEdgeType::en rhs) { return (lhs.m_e == rhs); } + inline bool operator== (OrderVEdgeType::en lhs, OrderVEdgeType rhs) { return (lhs == rhs.m_e); } + +//###################################################################### +// Graph types + +class OrderGraph : public V3Graph { +public: + OrderGraph() {} + virtual ~OrderGraph() {} + // Methods + virtual void loopsVertexCb(V3GraphVertex* vertexp); +}; + +//###################################################################### +// Vertex types + +class OrderEitherVertex : public V3GraphVertex { + AstScope* m_scopep; // Scope the vertex is in + AstSenTree* m_domainp; // Clock domain (NULL = to be computed as we iterate) + OrderLoopId m_inLoop; // Loop number vertex is in +public: + OrderEitherVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp) + : V3GraphVertex(graphp), m_scopep(scopep), m_domainp(domainp) + , m_inLoop(LOOPID_UNKNOWN) { + } + virtual ~OrderEitherVertex() {} + // Methods + virtual OrderVEdgeType type() const = 0; + virtual bool domainMatters() = 0; // Must be in same domain when cross edge to this vertex + virtual string dotName() const { return cvtToStr((void*)m_scopep)+"_"; } + // Accessors + void domainp(AstSenTree* domainp) { m_domainp = domainp; } + AstScope* scopep() const { return m_scopep; } + AstSenTree* domainp() const { return m_domainp; } + OrderLoopId inLoop() const { return m_inLoop; } + void inLoop(OrderLoopId inloop) { m_inLoop = inloop; } +}; + +class OrderInputsVertex : public OrderEitherVertex { +public: + OrderInputsVertex(V3Graph* graphp, AstSenTree* domainp) + : OrderEitherVertex(graphp, NULL, domainp) {} + virtual ~OrderInputsVertex() {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_INPUTS; } + virtual string name() const { return "*INPUTS*"; } + virtual string dotColor() const { return "green"; } + virtual string dotName() const { return ""; } + virtual bool domainMatters() { return false; } +}; + +class OrderSettleVertex : public OrderEitherVertex { +public: + OrderSettleVertex(V3Graph* graphp, AstSenTree* domainp) + : OrderEitherVertex(graphp, NULL, domainp) {} + virtual ~OrderSettleVertex() {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_SETTLE; } + virtual string name() const { return "*SETTLE*"; } + virtual string dotColor() const { return "green"; } + virtual string dotName() const { return ""; } + virtual bool domainMatters() { return true; } +}; + +class OrderLogicVertex : public OrderEitherVertex { + AstNode* m_nodep; + OrderMoveVertex* m_moveVxp; +public: + OrderLogicVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp, AstNode* nodep) + : OrderEitherVertex(graphp, scopep, domainp), m_nodep(nodep), m_moveVxp(NULL) {} + virtual ~OrderLogicVertex() {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_LOGIC; } + virtual bool domainMatters() { return true; } + // Accessors + virtual string name() const { return (cvtToStr((void*)m_nodep)+"\\n "+cvtToStr(nodep()->typeName())); } + AstNode* nodep() const { return m_nodep; } + virtual string dotColor() const { return "yellow"; } + OrderMoveVertex* moveVxp() const { return m_moveVxp; } + void moveVxp(OrderMoveVertex* moveVxp) { m_moveVxp = moveVxp; } +}; + +class OrderVarVertex : public OrderEitherVertex { + AstVarScope* m_varScp; + bool m_isClock; // Used as clock + OrderVarVertex* m_pilNewVertexp; // for processInsLoopNewVar +public: + OrderVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) + : OrderEitherVertex(graphp, scopep, NULL), m_varScp(varScp), m_isClock(false) + , m_pilNewVertexp(NULL) + {} + virtual ~OrderVarVertex() {} + virtual OrderVarVertex* clone (V3Graph* graphp) const = 0; + virtual OrderVEdgeType type() const = 0; + // Accessors + AstVarScope* varScp() const { return m_varScp; } + void isClock(bool clk) { m_isClock=clk; } + bool isClock() const { return m_isClock; } + OrderVarVertex* pilNewVertexp() const { return m_pilNewVertexp; } + void pilNewVertexp (OrderVarVertex* vertexp) { m_pilNewVertexp = vertexp; } +}; + +class OrderVarStdVertex : public OrderVarVertex { +public: + OrderVarStdVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) + : OrderVarVertex(graphp, scopep,varScp) {} + virtual ~OrderVarStdVertex() {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARSTD; } + virtual OrderVarVertex* clone (V3Graph* graphp) const { + return new OrderVarStdVertex(graphp, scopep(), varScp()); + } + virtual string name() const { return (cvtToStr((void*)varScp())+"\\n "+varScp()->name());} + virtual string dotColor() const { return "skyblue"; } + virtual bool domainMatters() { return true; } +}; +class OrderVarPreVertex : public OrderVarVertex { +public: + OrderVarPreVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) + : OrderVarVertex(graphp, scopep,varScp) {} + virtual ~OrderVarPreVertex() {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARPRE; } + virtual OrderVarVertex* clone (V3Graph* graphp) const { + return new OrderVarPreVertex(graphp, scopep(), varScp()); + } + virtual string name() const { return (cvtToStr((void*)varScp())+" PRE\\n "+varScp()->name());} + virtual string dotColor() const { return "lightblue"; } + virtual bool domainMatters() { return false; } +}; +class OrderVarPostVertex : public OrderVarVertex { +public: + OrderVarPostVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) + : OrderVarVertex(graphp, scopep,varScp) {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARPOST; } + virtual ~OrderVarPostVertex() {} + virtual OrderVarVertex* clone (V3Graph* graphp) const { + return new OrderVarPostVertex(graphp, scopep(), varScp()); + } + virtual string name() const { return (cvtToStr((void*)varScp())+" POST\\n "+varScp()->name());} + virtual string dotColor() const { return "CadetBlue"; } + virtual bool domainMatters() { return false; } +}; +class OrderVarPordVertex : public OrderVarVertex { +public: + OrderVarPordVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) + : OrderVarVertex(graphp, scopep,varScp) {} + virtual OrderVarVertex* clone (V3Graph* graphp) const { + return new OrderVarPordVertex(graphp, scopep(), varScp()); + } + virtual ~OrderVarPordVertex() {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARPORD; } + virtual string name() const { return (cvtToStr((void*)varScp())+" PORD\\n "+varScp()->name());} + virtual string dotColor() const { return "NavyBlue"; } + virtual bool domainMatters() { return false; } +}; +class OrderVarSettleVertex : public OrderVarVertex { +public: + OrderVarSettleVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) + : OrderVarVertex(graphp, scopep,varScp) {} + virtual ~OrderVarSettleVertex() {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARSETTLE; } + virtual OrderVarVertex* clone (V3Graph* graphp) const { + return new OrderVarSettleVertex(graphp, scopep(), varScp()); + } + virtual string name() const { return (cvtToStr((void*)varScp())+" STL\\n "+varScp()->name());} + virtual string dotColor() const { return "PowderBlue"; } + virtual bool domainMatters() { return false; } +}; + +//###################################################################### +//--- Looping constructs + +class OrderLoopBeginVertex : public OrderLogicVertex { + // A vertex can never be under two loops... + // However, a LoopBeginVertex is not "under" the loop per se, and it may be under another loop. + OrderLoopId m_loopId; // Arbitrary # to ID this loop + uint32_t m_loopColor; // Color # of loop (for debug) +public: + OrderLoopBeginVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp, AstUntilStable* nodep, + OrderLoopId loopId, uint32_t loopColor) + : OrderLogicVertex(graphp, scopep, domainp, nodep) + , m_loopId(loopId), m_loopColor(loopColor) { + } + virtual ~OrderLoopBeginVertex() {} + // Methods + virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_LOOPBEGIN; } + virtual string name() const { return "LoopBegin_"+cvtToStr(loopId())+"_c"+cvtToStr(loopColor()); } + virtual bool domainMatters() { return true; } + virtual string dotColor() const { return "blue"; } + AstUntilStable* untilp() const { return nodep()->castUntilStable(); } + OrderLoopId loopId() const { return m_loopId; } + uint32_t loopColor() const { return m_loopColor; } +}; + +class OrderLoopEndVertex : public OrderLogicVertex { + // A end vertex points to the *same nodep* as the Begin, + // as we need it to be a logic vertex for moving, but don't need a permanent node. + // We won't add to the output graph though, so it shouldn't matter. + OrderLoopBeginVertex* m_beginVertexp; // Corresponding loop begin +public: + OrderLoopEndVertex(V3Graph* graphp, OrderLoopBeginVertex* beginVertexp) + : OrderLogicVertex(graphp, beginVertexp->scopep(), beginVertexp->domainp(), beginVertexp->nodep()) + , m_beginVertexp(beginVertexp) { + inLoop(beginVertexp->loopId()); + } + virtual ~OrderLoopEndVertex() {} + // Methods + virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_LOOPEND; } + virtual string name() const { return "LoopEnd_"+cvtToStr(inLoop())+"_c"+cvtToStr(loopColor()); } + virtual bool domainMatters() { return false; } + virtual string dotColor() const { return "blue"; } + OrderLoopBeginVertex* beginVertexp() const { return m_beginVertexp; } + uint32_t loopColor() const { return beginVertexp()->loopColor(); } +}; + +//###################################################################### +//--- Following only under the move graph, not the main graph + +class OrderMoveVertex : public V3GraphVertex { + typedef enum {POM_WAIT, POM_READY, POM_MOVED} OrderMState; + + OrderLogicVertex* m_logicp; + OrderMState m_state; // Movement state + OrderMoveDomScope* m_domScopep; // Domain/scope list information + +protected: + friend class OrderVisitor; + // These only contain the "next" item, + // for the head of the list, see the same var name under OrderVisitor + V3ListEnt m_pomWaitingE; // List of nodes needing inputs to become ready + V3ListEnt m_readyVerticesE;// List of ready under domain/scope +public: + OrderMoveVertex(V3Graph* graphp, OrderLogicVertex* logicp) + : V3GraphVertex(graphp), m_logicp(logicp), m_state(POM_WAIT), m_domScopep(NULL) {} + virtual ~OrderMoveVertex() {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_MOVE; } + virtual string dotColor() const { return logicp()->dotColor(); } + virtual string name() const { + string nm = logicp()->name(); + nm += (string("\\nMV:") + +" lp="+cvtToStr(logicp()->inLoop()) + +" d="+cvtToStr((void*)logicp()->domainp()) + +" s="+cvtToStr((void*)logicp()->scopep())); + return nm; + } + // ACCESSORS + OrderLogicVertex* logicp() const { return m_logicp; } + bool isWait() const { return m_state==POM_WAIT; } + void setReady() { + UASSERT(m_state==POM_WAIT, "Wait->Ready on node not in proper state\n"); + m_state = POM_READY; + } + void setMoved() { + UASSERT(m_state==POM_READY, "Ready->Moved on node not in proper state\n"); + m_state = POM_MOVED; + } + OrderMoveDomScope* domScopep() const { return m_domScopep; } + OrderMoveVertex* pomWaitingNextp() const { return m_pomWaitingE.nextp(); } + void domScopep(OrderMoveDomScope* ds) { m_domScopep=ds; } +}; + +//###################################################################### +// Edge types + +class OrderEdge : public V3GraphEdge { +public: + OrderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, + int weight, bool cutable=false) + : V3GraphEdge(graphp, fromp, top, weight, cutable) {} + virtual ~OrderEdge() {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_STD; } + // When ordering combo blocks with stronglyConnected, follow edges not involving pre/pos variables + virtual bool followComboConnected() const { return true; } + virtual bool followSequentConnected() const { return true; } + virtual OrderEdge* clone (V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const { + return new OrderEdge(graphp, fromp, top, weight(), cutable()); + } + static bool followComboConnected(const V3GraphEdge* edgep) { + const OrderEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) v3fatalSrc("Following edge of non-OrderEdge type"); + return (oedgep->followComboConnected()); + } + static bool followSequentConnected(const V3GraphEdge* edgep) { + const OrderEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) v3fatalSrc("Following edge of non-OrderEdge type"); + return (oedgep->followSequentConnected()); + } +}; + +class OrderChangeDetEdge : public OrderEdge { + // Edge created from variable to OrderLoopEndVertex + // Indicates a change detect will be required for this loop construct +public: + OrderChangeDetEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) + : OrderEdge(graphp, fromp, top, WEIGHT_MEDIUM, false) {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_CHANGEDET; } + virtual OrderEdge* clone (V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const { + return new OrderChangeDetEdge(graphp, fromp, top); + } + virtual ~OrderChangeDetEdge() {} + virtual string dotColor() const { return "blue"; } +}; + +class OrderComboCutEdge : public OrderEdge { + // Edge created from output of combo logic + // Breakable if the output var is also a input, + // in which case we'll need a change detect loop around this var. +public: + OrderComboCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) + : OrderEdge(graphp, fromp, top, WEIGHT_COMBO, CUTABLE) {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_COMBOCUT; } + virtual OrderEdge* clone (V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const { + return new OrderComboCutEdge(graphp, fromp, top); + } + virtual ~OrderComboCutEdge() {} + virtual string dotColor() const { return "yellowGreen"; } + virtual bool followComboConnected() const { return true; } + virtual bool followSequentConnected() const { return true; } +}; + +class OrderPostCutEdge : public OrderEdge { + // Edge created from output of post assignment + // Breakable if the output var feeds back to input combo logic or another clock pin + // in which case we'll need a change detect loop around this var. +public: + OrderPostCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) + : OrderEdge(graphp, fromp, top, WEIGHT_COMBO, CUTABLE) {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_POSTCUT; } + virtual OrderEdge* clone (V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const { + return new OrderPostCutEdge(graphp, fromp, top); + } + virtual ~OrderPostCutEdge() {} + virtual string dotColor() const { return "PaleGreen"; } + virtual bool followComboConnected() const { return false; } + virtual bool followSequentConnected() const { return true; } +}; + +class OrderPreCutEdge : public OrderEdge { + // Edge created from var_PREVAR->consuming logic vertex + // Always breakable, just results in performance loss + // in which case we can't optimize away the pre/post delayed assignments +public: + OrderPreCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) + : OrderEdge(graphp, fromp, top, WEIGHT_PRE, CUTABLE) {} + virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_PRECUT; } + virtual OrderEdge* clone (V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const { + return new OrderPreCutEdge(graphp, fromp, top); + } + virtual ~OrderPreCutEdge() {} + virtual string dotColor() const { return "khaki"; } + virtual bool followComboConnected() const { return false; } + virtual bool followSequentConnected() const { return false; } +}; + diff --git a/src/V3Param.cpp b/src/V3Param.cpp new file mode 100644 index 000000000..6345d8054 --- /dev/null +++ b/src/V3Param.cpp @@ -0,0 +1,348 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Replicate modules for parameterization +// +// 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. +// +//************************************************************************* +// PARAM TRANSFORMATIONS: +// Top down traversal: +// For each cell: +// If parameterized, +// Determine all parameter widths, constant values +// Clone module cell calls, renaming with __{par1}_{par2}_... +// Substitute constants for cell's module's parameters +// Relink pins and cell to point to new module +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Param.h" +#include "V3Read.h" +#include "V3Ast.h" +#include "V3Case.h" +#include "V3Const.h" +#include "V3Width.h" +#include "V3Signed.h" +#include "V3Unroll.h" + +//###################################################################### +// Param state, as a visitor of each AstNode + +class ParamVisitor : public AstNVisitor { +private: + // NODE STATE + // AstModule::user4() // bool True if parameters numbered + // AstVar::user4() // int Global parameter number (for naming new module) + // STATE + typedef std::map VarCloneMap; + struct ModInfo { + AstModule* m_modp; // Module with specified name + VarCloneMap m_cloneMap; // Map of old-varp -> new cloned varp + ModInfo(AstModule* modp) { m_modp=modp; } + }; + typedef std::map ModNameMap; + ModNameMap m_modNameMap; // Hash of created module flavors by name + + typedef std::map LongMap; + LongMap m_longMap; // Hash of very long names to unique identity number + int m_longId; + + //int debug() { return 9; } + + // METHODS + void makeSmallNames(AstModule* modp) { + vector usedLetter; usedLetter.resize(256); + // Pass 1, assign first letter to each gparam's name + for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) { + if (AstVar* varp = stmtp->castVar()) { + if (varp->isGParam()) { + char ch = varp->name()[0]; + ch = toupper(ch); if (ch<'A' || ch>'Z') ch='Z'; + varp->user4(usedLetter[ch]*256 + ch); + usedLetter[ch]++; + } + } + } + } + string paramSmallName(AstModule* modp, AstVar* varp) { + if (!varp->user4()) { + makeSmallNames(modp); + } + int index = varp->user4()/256; + char ch = varp->user4()&255; + string st = cvtToStr(ch); + while (index) { + st += cvtToStr(char((index%26)+'A')); + index /= 26; + } + return st; + } + void relinkPins(VarCloneMap* clonemapp, AstPin* startpinp) { + for (AstPin* pinp = startpinp; pinp; pinp=pinp->nextp()->castPin()) { + if (!pinp->modVarp()) pinp->v3fatalSrc("Not linked?\n"); + // Find it in the clone structure + //UINFO(8,"Clone find 0x"<modVarp()<find(pinp->modVarp()); + UASSERT(cloneiter != clonemapp->end(), "Couldn't find pin in clone list"); + pinp->modVarp(cloneiter->second); + } + } + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + AstNode::user4ClearTree(); + // Modules must be done in top-down-order + nodep->iterateChildren(*this); + } + virtual void visit(AstModule* nodep, AstNUser*) { + UINFO(4," MOD "<iterateChildren(*this); + } + virtual void visit(AstCell* nodep, AstNUser*); + + // Generate Statements + virtual void visit(AstGenerate* nodep, AstNUser*) { + if (debug()>=9) nodep->dumpTree(cout,"-genin: "); + nodep->iterateChildren(*this); + // After expanding the generate, all statements under it can be moved + // up, and the generate block deleted as it's not relevant + AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext(); + nodep->replaceWith(stmtsp); + nodep->deleteTree(); nodep=NULL; + if (debug()>=9) stmtsp->dumpTree(cout,"-genout: "); + } + virtual void visit(AstGenIf* nodep, AstNUser*) { + V3Width::widthParams(nodep); // Param typed widthing will NOT recurse the body + V3Signed::signedParams(nodep); + V3Const::constifyParam(nodep->condp()); + if (AstConst* constp = nodep->condp()->castConst()) { + AstNode* keepp = (constp->isZero() + ? nodep->elsesp() + : nodep->ifsp()); + if (keepp) { + keepp->unlinkFrBackWithNext(); + nodep->replaceWith(keepp); + } else { + nodep->unlinkFrBack(); + } + nodep->deleteTree(); nodep=NULL; + } else { + nodep->condp()->v3error("Generate If condition must evaluate to constant"); + } + } + virtual void visit(AstGenFor* nodep, AstNUser*) { + // We parse a very limited form of FOR, so we don't need to do a full + // simulation to unroll the loop + V3Width::widthParams(nodep); // Param typed widthing will NOT recurse the body + V3Signed::signedParams(nodep); + V3Unroll::unrollGen(nodep); nodep=NULL; + } + virtual void visit(AstGenCase* nodep, AstNUser*) { + AstNode* keepp = NULL; + V3Case::caseLint(nodep); + V3Width::widthParams(nodep); // Param typed widthing will NOT recurse the body + V3Signed::signedParams(nodep); + V3Const::constifyParam(nodep->exprp()); + AstConst* exprp = nodep->exprp()->castConst(); + // Constify + for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) { + for (AstNode* ep = itemp->condsp(); ep; ) { + AstNode* nextp = ep->nextp(); //May edit list + V3Const::constifyParam(ep); + ep = nextp; + } + } + // Item match + for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) { + if (!itemp->isDefault()) { + for (AstNode* ep = itemp->condsp(); ep; ep=ep->nextp()) { + if (AstConst* ccondp = ep->castConst()) { + V3Number match (nodep->fileline(), 1); + match.opEq(ccondp->num(), exprp->num()); + if (!keepp && match.isNeqZero()) { + keepp = itemp->bodysp(); + } + } else { + itemp->v3error("Generate Case item does not evaluate to constant"); + } + } + } + } + // Else default match + for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) { + if (itemp->isDefault()) { + if (!keepp) keepp=itemp->bodysp(); + } + } + // Replace + if (keepp) { + keepp->unlinkFrBackWithNext(); + nodep->replaceWith(keepp); + } + else nodep->unlinkFrBack(); + nodep->deleteTree(); nodep=NULL; + } + + // Default: Just iterate + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + ParamVisitor(AstNetlist* nodep) { + m_longId = 0; + // + nodep->accept(*this); + } + virtual ~ParamVisitor() {} +}; + +//---------------------------------------------------------------------- +// VISITs + +void ParamVisitor::visit(AstCell* nodep, AstNUser*) { + // Cell: Check for parameters in the instantiation. + if (!nodep->modp()) { nodep->dumpTree(cout,"error:"); nodep->v3fatalSrc("Not linked?"); } + if (nodep->paramsp()) { + UINFO(4,"De-parameterize: "<9) nodep->dumpTree(cout,"cell:\t"); + // Evaluate all module constants + V3Const::constifyParam(nodep); + // Make sure constification worked + // Must be a separate loop, as constant conversion may have changed some pointers. + //if (debug()) nodep->dumpTree(cout,"cel2:\t"); + string longname = nodep->modp()->name(); + longname += "_"; + if (debug()>8) nodep->paramsp()->dumpTreeAndNext(cout,"-cellparams:\t"); + for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) { + if (!pinp) nodep->v3fatalSrc("Non pin under cell params\n"); + AstVar* modvarp = pinp->modVarp(); + if (!modvarp) { + pinp->v3error("Parameter not found in sub-module: Param "<name()<<" of "<prettyName()); + } else if (!modvarp->isGParam()) { + pinp->v3error("Attempted parameter setting of non-parameter: Param "<name()<<" of "<prettyName()); + } else { + AstConst* constp = pinp->exprp()->castConst(); + if (!constp) { + //if (debug()) pinp->dumpTree(cout,"error:"); + pinp->v3error("Can't convert defparam value to constant: Param "<name()<<" of "<prettyName()); + } else { + longname += "_" + paramSmallName(nodep->modp(),pinp->modVarp())+constp->num().ascii(false); + } + } + } + + // + // If the name is very long, we don't want to overwhelm the filename limit + // We don't do this always, as it aids debugability to have intuitive naming. + string newname = longname; + if (longname.length()>30) { + LongMap::iterator iter = m_longMap.find(longname); + if (iter != m_longMap.end()) { + newname = iter->second; + } else { + newname = nodep->modp()->name(); + newname += "__pi"+cvtToStr(++m_longId); // We use all upper case above, so lower here can't conflict + m_longMap.insert(make_pair(longname, newname)); + } + } + UINFO(4,"Name: "<modp()->name()<<"->"<"<second.m_modp; + if (!modp) { + // Deep clone of new module + // Note all module internal variables will be re-linked to the new modules by clone + // However links outside the module (like on the upper cells) will not. + modp = nodep->modp()->cloneTree(false)->castModule(); + modp->name(newname); + nodep->modp()->addNextHere(modp); // Keep tree sorted by cell occurrences + + m_modNameMap.insert(make_pair(modp->name(), ModInfo(modp))); + iter = m_modNameMap.find(newname); + VarCloneMap* clonemapp = &(iter->second.m_cloneMap); + UINFO(4," De-parameterize to new: "<stmtsp(); stmtp; stmtp = stmtp->nextp()) { + if (AstVar* varp = stmtp->castVar()) { + if (varp->isIO() || varp->isGParam()) { + // Cloning saved a pointer to the new node for us, so just follow that link. + AstVar* oldvarp = varp->clonep()->castVar(); + //UINFO(8,"Clone list 0x"< 0x"<<(uint32_t)varp<insert(make_pair(oldvarp, varp)); + } + } + } + + // Relink parameter vars to the new module + relinkPins(clonemapp, nodep->paramsp()); + + // Assign parameters to the constants specified + for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) { + AstVar* modvarp = pinp->modVarp(); + if (modvarp) { + AstConst* constp = pinp->exprp()->castConst(); + // Remove any existing parameter + if (modvarp->initp()) modvarp->initp()->unlinkFrBack()->deleteTree(); + // Set this parameter to value requested by cell + modvarp->initp(constp->cloneTree(false)); + } + } + } else { + UINFO(4," De-parameterize to old: "<modp(modp); + nodep->modName(newname); + + // Delete the parameters from the cell; they're not relevant any longer. + { + AstNRelinker delHandle; + nodep->paramsp()->unlinkFrBackWithNext(&delHandle); + delHandle.oldp()->deleteTree(); + } + + // We need to relink the pins to the new module + VarCloneMap* clonemapp = &(iter->second.m_cloneMap); + relinkPins(clonemapp, nodep->pinsp()); + + UINFO(8," Done with "<incLineno(); } + // Called by V3PreProc.cpp to inform lexer + void setStateDefArg(); + void setStateDefValue(); + void setStateIncFilename(); + void unputString(const char* textp); +}; + +#endif // Guard diff --git a/src/V3PreLex.l b/src/V3PreLex.l new file mode 100644 index 000000000..dca86e82c --- /dev/null +++ b/src/V3PreLex.l @@ -0,0 +1,225 @@ +/* $Id$ -*- C++ -*- */ +/************************************************************************** + * DESCRIPTION: Verilator: Flex verilog preprocessor + * + * 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. + * + ************************************************************************** + * Do not use Flex in C++ mode. It has bugs with yyunput() which result in + * lost characters. + **************************************************************************/ + +%option noyywrap align interactive +%option stack +%option noc++ +%option prefix="V3PreLex" +%{ + +#include "V3PreProc.h" +#include "V3PreLex.h" + +V3PreLex* V3PreLex::s_currentLexp = NULL; // Current lexing point + +// Prevent conflicts from perl version +static void linenoInc() {V3PreLex::s_currentLexp->incLineno();} +static bool optPsl() { return V3PreProc::optPsl(); } +static bool pedantic() { return V3PreLex::s_currentLexp->m_pedantic; } +static void yyerror(char* msg) { V3PreLex::s_currentLexp->m_curFilelinep->v3error(msg); } +static void appendDefValue(char* t,int l) { V3PreLex::s_currentLexp->appendDefValue(t,l); } +static int pslParenLevel() { return V3PreLex::s_currentLexp->m_pslParenLevel; } +static void pslParenLevelInc() { V3PreLex::s_currentLexp->m_pslParenLevel++; } +static void pslParenLevelDec() { if (pslParenLevel()) V3PreLex::s_currentLexp->m_pslParenLevel--; } +static bool pslMoreNeeded() { return V3PreLex::s_currentLexp->m_pslMoreNeeded; } +static void pslMoreNeeded(bool flag) { V3PreLex::s_currentLexp->m_pslMoreNeeded = flag; } + +/**********************************************************************/ +%} + +%x PSLONEM +%x PSLONEE +%x PSLMULM +%x PSLMUL1 +%x CMTONEM +%x CMTBEGM +%x CMTMODE +%x STRMODE +%x DEFMODE +%x ARGMODE +%x INCMODE + +ws [ \t\r\f] +newline [\n] +quote [\"] +backslash [\\] +symb [a-zA-Z_][a-zA-Z0-9_$]* +psl [p]sl + + /**************************************************************/ +%% + +^{ws}*"`line"{ws}+.*{newline} { V3PreLex::s_currentLexp->lineDirective(yytext); } + + /* Special directives we recognise */ +"`include" { return(VP_INCLUDE); } +"`ifdef" { return(VP_IFDEF); } +"`ifndef" { return(VP_IFNDEF); } +"`else" { return(VP_ELSE); } +"`elsif" { return(VP_ELSIF); } +"`endif" { return(VP_ENDIF); } +"`undef" { return(VP_UNDEF); } +"`define" { return(VP_DEFINE); } + + /* Optional directives we recognise */ +"`__FILE__" { if (!pedantic()) { + yytext = (char*)V3PreLex::s_currentLexp->m_curFilelinep->cfilename(); + yyleng = strlen(yytext); return (VP_TEXT); + } else return(VP_DEFREF); } +"`__LINE__" { if (!pedantic()) { + static char buf[10]; + sprintf(buf, "%d",V3PreLex::s_currentLexp->m_curFilelinep->lineno()); + yytext = buf; yyleng = strlen(yytext); return (VP_TEXT); + } else return(VP_DEFREF); } +"`error" { if (!pedantic()) return (VP_ERROR); else return(VP_DEFREF); } + + /* Pass-through strings */ +{quote} { yy_push_state(STRMODE); yymore(); } +<> { linenoInc(); yyerror("EOF in unterminated string"); yyleng=0; yyterminate(); } +{newline} { linenoInc(); yyerror("Unterminated string"); BEGIN(INITIAL); } +[^\"\\] { yymore(); } +{backslash}. { yymore(); } +{quote} { yy_pop_state(); + if (V3PreLex::s_currentLexp->m_parenLevel) appendDefValue(yytext,yyleng); + else return (VP_STRING); } + + /* Pass-through include <> filenames */ +<> { linenoInc(); yyerror("EOF in unterminated include filename"); yyleng=0; yyterminate(); } +{newline} { linenoInc(); yyerror("Unterminated include filename"); BEGIN(INITIAL); } +[^\>\\] { yymore(); } +{backslash}. { yymore(); } +[\>] { yy_pop_state(); return (VP_STRING); } + + /* Reading definition */ +"/*" { yy_push_state(CMTMODE); yymore(); } +"//"[^\n]* { return (VP_COMMENT);} +<> { linenoInc(); yyerror("EOF (missing return?) in define value"); yyleng=0; yyterminate(); } +{newline} { linenoInc(); + yy_pop_state(); + return (VP_DEFVALUE); } /* Note contains a return */ +[^\/\*\n\m\\]+ | +[\\][^\n] | +. { appendDefValue(yytext,yyleng); } +[\\]\n { linenoInc(); appendDefValue("\n",1); } + + /* Define arguments */ +"/*" { yy_push_state(CMTMODE); yymore(); } +"//"[^\n]* { return (VP_COMMENT);} +<> { yyerror("EOF in define argument list\n"); yyleng = 0; yyterminate(); } +{newline} { linenoInc(); yytext="\n"; yyleng=1; return(VP_WHITE); } +{quote} { yy_push_state(STRMODE); yymore(); } +[(] { V3PreLex::s_currentLexp->m_parenLevel++; appendDefValue(yytext,yyleng); } +[,)] { if (V3PreLex::s_currentLexp->m_parenLevel>1) { + appendDefValue(yytext,yyleng); + if (yytext[0]==')') V3PreLex::s_currentLexp->m_parenLevel--; + } else { + unput(yytext[0]); yy_pop_state(); return (VP_DEFARG); + }} +[^\/\*\n\m\\(,)\"]+ | +. { appendDefValue(yytext,yyleng); } + + /* One line comments. */ +"//"{ws}*{psl} { if (optPsl()) { pslMoreNeeded(true); yy_push_state(PSLONEM); return(VP_PSL); } + else { yy_push_state(CMTONEM); yymore(); } } +"//"{newline} { linenoInc(); yytext="\n"; yyleng=1; return (VP_WHITE); } +"//" { if (pslMoreNeeded()) { pslMoreNeeded(true); yy_push_state(PSLONEM); return(VP_PSL); } + else { yy_push_state(CMTONEM); yymore(); } } +[^\n]* { yy_pop_state(); return (VP_COMMENT); } + + /* Psl oneline comments */ +[{(] { pslParenLevelInc(); return (VP_TEXT); } +[})] { pslParenLevelDec(); return (VP_TEXT); } +[;] { if (!pslParenLevel()) {BEGIN PSLONEE; pslMoreNeeded(false);} return (VP_TEXT); } +<> { yyerror("EOF in '/* ... */' psl comment\n"); yyleng=0; yyterminate(); } +{newline} { linenoInc(); yy_pop_state(); return(VP_WHITE); } + + /* Completed psl oneline comments */ +{newline} { linenoInc(); yy_pop_state(); return(VP_WHITE); } +{ws}+ { yymore(); } +. { yyerror("Unexpected text following psl assertion\n"); } + + /* C-style comments. */ + /* We distinguish between the start of a comment, and later, so we may find a "psl" prefix */ +"/*" { yy_push_state(optPsl() ? CMTBEGM : CMTMODE); yymore(); } +{psl} { yyleng -= 3; BEGIN PSLMUL1; return (VP_COMMENT); } +{ws}+ { yymore(); } +"*/" { yy_pop_state(); return(VP_COMMENT); } +{newline} { linenoInc(); yymore(); } +<> { yyerror("EOF in '/* ... */' block comment\n"); yyleng=0; yyterminate(); } +. { BEGIN CMTMODE; yymore(); } /* Non 'psl' beginning in comment */ +. { yymore(); } + + /* Psl C-style comments. */ +.|{newline} { yyless(0); BEGIN PSLMULM; return(VP_PSL); } +"*/" { yy_pop_state(); return(VP_COMMENT); } +"//"[^\n]* { return (VP_COMMENT); } /* Comments inside block comments get literal inclusion (later removal) */ +<> { yyerror("EOF in '/* ... */' psl comment\n"); yyleng=0; yyterminate(); } + + /* Define calls */ +"`"{symb} { return (VP_DEFREF); } + + /* Generics */ +{newline} { linenoInc(); return(VP_WHITE); } +{symb} { return (VP_SYMBOL); } +{ws}+ { return (VP_WHITE); } +. { return (VP_TEXT); } +%% + +void V3PreLex::setStateDefArg() { + // Enter define substitution argument state + yy_push_state(ARGMODE); + m_parenLevel = 1; + m_defValue = ""; +} + +void V3PreLex::setStateDefValue() { + // Enter define value state + yy_push_state(DEFMODE); + m_parenLevel = 0; + m_defValue = ""; +} + +void V3PreLex::setStateIncFilename() { + // Enter include <> filename state + yy_push_state(INCMODE); + yymore(); +} + +void V3PreLex::unputString(const char* textp) { + // Add characters to input stream in back-to-front order + const char* cp; + for (cp = textp; *cp; cp++); + for (cp--; cp >= textp; cp--) { + unput(*cp); + } +} + +void V3PreLex::appendDefValue(const char* textp, int len) { + // Append given text to current definition value being formed + m_defValue.append(textp,len); +} + +void V3PreLex::lineDirective(const char* textp) { + m_curFilelinep->lineDirective(textp); + // Make sure we have a dependency on whatever file was specified + V3File::addSrcDepend(m_curFilelinep->filename()); +} diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp new file mode 100644 index 000000000..37cb2a461 --- /dev/null +++ b/src/V3PreProc.cpp @@ -0,0 +1,895 @@ +// $Id$ -*- C++ -*- +//************************************************************************* +// DESCRIPTION: Verilog::Preproc: Internal implementation of default preprocessor +// +// Code available from: http://www.veripool.com/verilog-perl +// +// Authors: Wilson Snyder +// +//************************************************************************* +// +// Copyright 2000-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 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. +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#include "V3Error.h" +#include "V3Global.h" +#include "V3File.h" +#include "V3PreLex.h" +#include "V3PreProc.h" +#include "V3PreShell.h" + +//====================================================================== +// Build in LEX script + +#define yyFlexLexer V3Lexer +#include "V3PreLex.yy.cpp" +#undef yyFlexLexer + +//YYSTYPE yylval; + +//************************************************************************* + +class V3Define { + // Define class. One for each define. + //string m_name; // Name of the define (list is keyed by this) + FileLine* m_fileline; // Where it was declared + string m_value; // Value of define + string m_params; // Parameters +public: + V3Define(FileLine* fl, const string& value, const string& params) + : m_fileline(fl), m_value(value), m_params(params) {} + FileLine* fileline() const { return m_fileline; } + string value() const { return m_value; } + string params() const { return m_params; } +}; + +//************************************************************************* +// Data for a preprocessor instantiation. + +struct V3PreProcImp : public V3PreProc { + // TYPES + typedef std::map DefinesMap; + + // debug() -> see V3PreShellImp::debug + + // STATE + V3PreLex* m_lexp; // Current lexer state (NULL = closed) + stack m_includeStack; // Stack of includers above current m_lexp + + enum ProcState { ps_TOP, ps_DEFNAME, ps_DEFVALUE, ps_DEFPAREN, ps_DEFARG, + ps_INCNAME, ps_ERRORNAME }; + ProcState m_state; // Current state of parser + int m_stateFor; // Token state is parsing for + int m_off; // If non-zero, ifdef level is turned off, don't dump text + string m_lastSym; // Last symbol name found. + + // For getRawToken/ `line insertion + string m_lineCmt; // Line comment(s) to be returned + int m_lineAdd; // Empty lines to return to maintain line count + + // For defines + string m_defName; // Define last name being defined + string m_defParams; // Define parameter list for next expansion + stack m_ifdefStack; // Stack of true/false emitting evaluations + vector m_defArgs; // List of define arguments + unsigned m_defDepth; // How many `defines deep + + // Defines list + DefinesMap m_defines; // Map of defines + + // For getline() + string m_lineChars; // Characters left for next line + + void v3errorEnd(ostringstream& str) { + fileline()->v3errorEnd(str); + } + + const char* tokenName(int tok); + int getRawToken(); + int getToken(); + void parseTop(); + void parseUndef(); + +private: + // Internal methods + void eof(); + string defineSubst(); + void addLineComment(int enter_exit_level); + + bool defExists(const string& name); + string defValue(const string& name); + string defParams(const string& name); + FileLine* defFileline(const string& name); + + bool commentTokenMatch(string& cmdr, const char* strg); + + void parsingOn() { + m_off--; + if (m_off<0) fileline()->v3fatalSrc("Underflow of parsing cmds"); + if (!m_off) addLineComment(0); + } + void parsingOff() { m_off++; } + +public: + // METHODS, called from upper level shell + virtual void openFile(FileLine* fl, const string& filename); + virtual bool isEof() const { return (m_lexp==NULL); } + virtual string getline(); + virtual void insertUnreadback(const string& text) { m_lineCmt += text; } + + // METHODS, callbacks + virtual void comment(const string& cmt); // Comment detected (if keepComments==2) + virtual void include(const string& filename); // Request a include file be processed + virtual void undef (const string& name); + virtual void define (FileLine* fl, const string& name, const string& value, const string& params); + virtual string removeDefines(const string& text); // Remove defines in a text string + + // CONSTRUCTORS + V3PreProcImp(FileLine* fl) : V3PreProc(fl) { + m_lexp = NULL; // Closed. + m_state = ps_TOP; + m_defName = ""; + m_off = 0; + m_lineChars = ""; + m_lastSym = ""; + m_lineAdd = 0; + m_defDepth = 0; + } +}; + +//************************************************************************* +// Creation + +V3PreProc* V3PreProc::createPreProc(FileLine* fl) { + return new V3PreProcImp(fl); +} + +bool V3PreProc::optPsl() { + return v3Global.opt.psl(); +} + +//************************************************************************* +// Defines + +void V3PreProcImp::undef(const string& name) { + m_defines.erase(name); +} +bool V3PreProcImp::defExists(const string& name) { + DefinesMap::iterator iter = m_defines.find(name); + if (iter == m_defines.end()) return false; + return true; +} +string V3PreProcImp::defValue(const string& name) { + DefinesMap::iterator iter = m_defines.find(name); + if (iter == m_defines.end()) { + fileline()->v3error("Define or directive not defined: `"+name); + return ""; + } + return iter->second.value(); +} +string V3PreProcImp::defParams(const string& name) { + DefinesMap::iterator iter = m_defines.find(name); + if (iter == m_defines.end()) { + fileline()->v3error("Define or directive not defined: `"+name); + return ""; + } + return iter->second.params(); +} +FileLine* V3PreProcImp::defFileline(const string& name) { + DefinesMap::iterator iter = m_defines.find(name); + if (iter == m_defines.end()) return false; + return iter->second.fileline(); +} +void V3PreProcImp::define(FileLine* fl, const string& name, const string& value, const string& params) { + UINFO(4,"DEFINE '"<v3error("Define already exists: "<v3error("Previous definition is here."); + } + undef(name); + } + m_defines.insert(make_pair(name, V3Define(fl, value, params))); +} + +string V3PreProcImp::removeDefines(const string& sym) { + string val = "0_never_match"; + string rtnsym = sym; + for (int loopprevent=0; loopprevent<100; loopprevent++) { + string xsym = rtnsym; + if (xsym.substr(0,1)=="`") xsym.replace(0,1,""); + if (defExists(xsym)) { + val = defValue(xsym); + if (val != rtnsym) rtnsym=val; // Prevent infinite loop if have `define X X + else break; + } else break; + } + return rtnsym; // NA +} + +//********************************************************************** +// Comments + +void V3PreProcImp::include(const string& filename) { + // Include seen. Ask the preprocessor shell to call back around to us + V3PreShell::preprocInclude (fileline(), filename); +} + +bool V3PreProcImp::commentTokenMatch(string& cmdr, const char* strg) { + int len = strlen(strg); + if (0==strncmp(cmdr.c_str(), strg, len) + && (cmdr[len]=='\0' || isspace(cmdr[len]))) { + if (isspace(cmdr[len])) len++; + cmdr = cmdr.substr(len); + return true; + } else { + return false; + } +} + +void V3PreProcImp::comment(const string& text) { + // Comment detected. Only keep relevant data. + // if (text =~ m!^\/[\*\/]\s*[vV]erilator\s*(.*$)!) { + // string cmd = $1; + // cmd =~ s!\s*(\*\/)\s*$!!; + // cmd =~ s!\s+! !g; + // cmd =~ s!\s+$!!g; + const char* cp = text.c_str(); + if (cp[0]=='/' && (cp[1]=='/' || cp[1]=='*')) { + cp+=2; + } else return; + + while (isspace(*cp)) cp++; + + bool synth = false; + if ((cp[0]=='v' || cp[0]=='V') + && 0==(strncmp(cp+1,"erilator",8))) { + cp+=strlen("verilator"); + } else if (0==(strncmp(cp,"synopsys",strlen("synopsys")))) { + cp+=strlen("synopsys"); + synth = true; + } else if (0==(strncmp(cp,"cadence",strlen("cadence")))) { + cp+=strlen("cadence"); + synth = true; + } else if (0==(strncmp(cp,"ambit synthesis",strlen("ambit synthesis")))) { + cp+=strlen("ambit synthesis"); + synth = true; + } else { + return; + } + if (*cp && !isspace(*cp)) return; + + while (isspace(*cp)) cp++; + + const char* ep = cp+strlen(cp); + if (ep>cp && (ep[-1]=='/' || cp[-2]=='*')) ep-=2; + while (ep>cp && (isspace(ep[-1]))) ep--; + + string cmd (cp, ep-cp); + string::size_type pos; + while ((pos=cmd.find("\"")) != string::npos) + cmd.replace(pos, 1, " "); + while ((pos=cmd.find("\t")) != string::npos) + cmd.replace(pos, 1, " "); + while ((pos=cmd.find(" ")) != string::npos) + cmd.replace(pos, 2, " "); + + if (synth) { + if (v3Global.opt.assertOn()) { + // one_hot, one_cold, (full_case, parallel_case) + if (commentTokenMatch(cmd/*ref*/, "full_case")) { + insertUnreadback ("/*verilator full_case*/"); + } + if (commentTokenMatch(cmd/*ref*/, "parallel_case")) { + insertUnreadback ("/*verilator parallel_case*/"); + } + if (commentTokenMatch(cmd/*ref*/, "one_hot")) { + insertUnreadback ("/*verilator one_hot*/ "+cmd+";"); + } + if (commentTokenMatch(cmd/*ref*/, "one_cold")) { + insertUnreadback ("/*verilator one_cold*/ "+cmd+";"); + } + // else ignore the comment we don't recognize + } // else no assertions + } else { + insertUnreadback ("/*verilator "+cmd+"*/"); + } +} + +//********************************************************************** +// Parser Utilities + +const char* V3PreProcImp::tokenName(int tok) { + switch (tok) { + case VP_EOF : return("EOF"); + case VP_INCLUDE : return("INCLUDE"); + case VP_IFDEF : return("IFDEF"); + case VP_IFNDEF : return("IFNDEF"); + case VP_ENDIF : return("ENDIF"); + case VP_UNDEF : return("UNDEF"); + case VP_DEFINE : return("DEFINE"); + case VP_ELSE : return("ELSE"); + case VP_ELSIF : return("ELSIF"); + case VP_SYMBOL : return("SYMBOL"); + case VP_STRING : return("STRING"); + case VP_DEFVALUE : return("DEFVALUE"); + case VP_COMMENT : return("COMMENT"); + case VP_TEXT : return("TEXT"); + case VP_WHITE : return("WHITE"); + case VP_DEFREF : return("DEFREF"); + case VP_DEFARG : return("DEFARG"); + case VP_ERROR : return("ERROR"); + case VP_PSL : return("PSL"); + default: return("?"); + } +} + +string V3PreProcImp::defineSubst() { + // Substitute out defines in a argumented define reference. + // We could push the define text back into the lexer, but that's slow + // and would make recursive definitions and parameter handling nasty. + // + // Note we parse the definition parameters and value here. If a + // parameterized define is used many, many times, we could cache the + // parsed result. + UINFO(4,"defineSubstIn `"< argValueByName; + { // Parse argument list into map + unsigned numArgs=0; + string argName; + for (const char* cp=m_defParams.c_str(); *cp; cp++) { + if (*cp=='(') { + } else if (argName=="" && isspace(*cp)) { + } else if (isspace(*cp) || *cp==')' || *cp==',') { + if (argName!="") { + if (m_defArgs.size() >= numArgs) { + argValueByName[argName] = m_defArgs[numArgs]; + } + numArgs++; + //cout << " arg "<v3error("Define passed wrong number of arguments: "+m_defName+"\n"); + return " `"+m_defName+" "; + } + } + + string out = " "; + { // Parse substitution define using arguments + string argName; + string prev; + bool quote = false; + // Note we go through the loop once more at the NULL end-of-string + for (const char* cp=value.c_str(); (*cp) || argName!=""; cp=(*cp?cp+1:cp)) { + //cout << "CH "<<*cp<<" an "<::iterator iter = argValueByName.find(argName); + if (iter != argValueByName.end()) { + // Substitute + string subst = iter->second; + out += subst; + } else { + out += argName; + } + argName = ""; + } + if (!quote) { + // Check for `` only after we've detected end-of-argname + if (cp[0]=='`' && cp[1]=='`') { + //out += ""; // `` means to suppress the `` + cp++; + continue; + } + else if (cp[0]=='`' && cp[1]=='"') { + out += '"'; // `" means to put out a " without enabling quote mode (sort of) + cp++; + continue; + } + else if (cp[0]=='`' && cp[1]=='\\') { + out += '\\'; // `\ means to put out a backslash + cp++; + continue; + } + } + if (cp[0]=='\\' && cp[1]) { + out += cp[0]; // \{any} Put out literal next character + out += cp[1]; + cp++; + continue; + } + if (*cp=='"') quote=!quote; + if (*cp) out += *cp; + } + } + + out += " "; + UINFO(4,"defineSubstOut "<v3error("File not found: "+filename+"\n"); + return; + } + + if (m_lexp) { + // We allow the same include file twice, because occasionally it pops + // up, with guards preventing a real recursion. + if (m_includeStack.size()>V3PreProc::INCLUDE_DEPTH_MAX) { + fileline()->v3error("Recursive inclusion of file: "+filename); + return; + } + // There's already a file active. Push it to work on the new one. + m_includeStack.push(m_lexp); + addLineComment(0); + } + + m_lexp = new V3PreLex (fp); + m_lexp->m_keepComments = keepComments(); + m_lexp->m_pedantic = pedantic(); + m_lexp->m_curFilelinep = new FileLine(filename, 1); + m_fileline = m_lexp->m_curFilelinep; // Remember token start location + addLineComment(1); // Enter + + yy_flex_debug = (debug()>4)?1:0; + yy_switch_to_buffer(m_lexp->m_yyState); +} + +void V3PreProcImp::addLineComment(int enter_exit_level) { + if (lineDirectives()) { + char numbuf[20]; sprintf(numbuf, "%d", m_lexp->m_curFilelinep->lineno()); + char levelbuf[20]; sprintf(levelbuf, "%d", enter_exit_level); + string cmt = ((string)"\n`line "+numbuf + +" \""+m_lexp->m_curFilelinep->filename()+"\" " + +levelbuf+"\n"); + insertUnreadback(cmt); + } +} + +void V3PreProcImp::eof() { + // Remove current lexer + UINFO(4,fileline()<<"EOF!\n"); + addLineComment(2); // Exit + delete m_lexp; m_lexp=NULL; + // Perhaps there's a parent file including us? + if (!m_includeStack.empty()) { + // Back to parent. + m_lexp = m_includeStack.top(); m_includeStack.pop(); + addLineComment(0); + yy_switch_to_buffer(m_lexp->m_yyState); + } +} + +int V3PreProcImp::getRawToken() { + // Get a token from the file, whatever it may be. + while (1) { + next_tok: + if (m_lineAdd) { + m_lineAdd--; + yytext="\n"; yyleng=1; + return (VP_TEXT); + } + if (m_lineCmt!="") { + // We have some `line directive to return to the user. Do it. + static string rtncmt; // Keep the c string till next call + rtncmt = m_lineCmt; + yytext=(char*)rtncmt.c_str(); yyleng=rtncmt.length(); + m_lineCmt = ""; + if (m_state!=ps_DEFVALUE) return (VP_TEXT); + else { + V3PreLex::s_currentLexp->appendDefValue(yytext,yyleng); + goto next_tok; + } + } + if (isEof()) return (VP_EOF); + // Snarf next token from the file + m_fileline = m_lexp->m_curFilelinep; // Remember token start location + V3PreLex::s_currentLexp = m_lexp; // Tell parser where to get/put data + int tok = yylex(); + + if (debug()>4) { + char buf[10000]; strncpy(buf, yytext, yyleng); buf[yyleng] = '\0'; + for (char* cp=buf; *cp; cp++) if (*cp=='\n') *cp='$'; + fprintf (stderr, "%d: RAW %d %d: %-10s: %s\n", + fileline()->lineno(), m_off, m_state, tokenName(tok), buf); + } + + // On EOF, try to pop to upper level includes, as needed. + if (tok==VP_EOF) { + eof(); + goto next_tok; // Parse parent, or find the EOF. + } + + return tok; + } +} + +// Sorry, we're not using bison/yacc. It doesn't handle returning white space +// in the middle of parsing other tokens. + +int V3PreProcImp::getToken() { + // Return the next user-visible token in the input stream. + // Includes and such are handled here, and are never seen by the caller. + while (1) { + next_tok: + if (isEof()) return VP_EOF; + int tok = getRawToken(); + // Always emit white space and comments between tokens. + if (tok==VP_WHITE) return (tok); + if (tok==VP_COMMENT) { + if (!m_off) { + if (m_lexp->m_keepComments == KEEPCMT_SUB) { + string rtn; rtn.assign(yytext,yyleng); + comment(rtn); + } else { + return (tok); + } + } + // We're off or processed the comment specially. If there are newlines + // in it, we also return the newlines as TEXT so that the linenumber + // count is maintained for downstream tools + for (int len=0; lenv3error("`elsif with no matching `if\n"); + } else { + // Handle `else portion + bool lastEnable = m_ifdefStack.top(); m_ifdefStack.pop(); + if (!lastEnable) parsingOn(); + // Handle `if portion + bool enable = !lastEnable && defExists(m_lastSym); + UINFO(4,"Elsif "<setStateDefValue(); + } + else fileline()->v3fatalSrc("Bad case\n"); + goto next_tok; + } + else { + fileline()->v3error("Expecting define name. Found: "<m_defValue.length(); i++) { + if (m_lexp->m_defValue[i] == '\n') { + m_lexp->m_defValue[i] = ' '; + newlines += "\n"; + } + } + if (!m_off) { + string params; + if (m_lexp->m_defValue=="" || isspace(m_lexp->m_defValue[0])) { + // Define without parameters + } else if (m_lexp->m_defValue[0] == '(') { + string::size_type paren = m_lexp->m_defValue.find(")"); + if (paren == string::npos) { + fileline()->v3error("Missing ) to end define arguments."); + } else { + params = m_lexp->m_defValue.substr(0, paren+1); + m_lexp->m_defValue.replace(0, paren+1, ""); + } + } else { + fileline()->v3error("Missing space or paren to start define value."); + } + // Remove leading whitespace + unsigned leadspace = 0; + while (m_lexp->m_defValue.length() > leadspace + && isspace(m_lexp->m_defValue[leadspace])) leadspace++; + if (leadspace) m_lexp->m_defValue.erase(0,leadspace); + // Remove trailing whitespace + unsigned trailspace = 0; + while (m_lexp->m_defValue.length() > trailspace + && isspace(m_lexp->m_defValue[m_lexp->m_defValue.length()-1-trailspace])) trailspace++; + if (trailspace) m_lexp->m_defValue.erase(m_lexp->m_defValue.length()-trailspace,trailspace); + // Define it + UINFO(4,"Define "<m_defValue<m_defValue, params); + } + } else { + fileline()->v3fatalSrc("Bad define text\n"); + } + m_state = ps_TOP; + // DEFVALUE is terminated by a return, but lex can't return both tokens. + // Thus, we emit a return here. + yytext=(char*)(newlines.c_str()); yyleng=newlines.length(); + return(VP_WHITE); + } + case ps_DEFPAREN: { + if (tok==VP_TEXT && yyleng==1 && yytext[0]=='(') { + m_defArgs.clear(); + m_state = ps_DEFARG; + m_lexp->setStateDefArg(); + goto next_tok; + } else { + m_state = ps_TOP; + fileline()->v3error("Expecting ( to begin argument list for define reference `"< + m_state = ps_INCNAME; // Still + m_lexp->setStateIncFilename(); + goto next_tok; + } + else if (tok==VP_DEFREF) { + // Expand it, then state will come back here + break; + } + else { + m_state = ps_TOP; + fileline()->v3error("Expecting include filename. Found: "<v3error(m_lastSym); + } + goto next_tok; + } + else { + m_state = ps_TOP; + fileline()->v3error("Expecting `error string. Found: "<v3fatalSrc("Bad case\n"); + } + // Default is to do top level expansion of some tokens + switch (tok) { + case VP_INCLUDE: + m_state = ps_INCNAME; m_stateFor = tok; + goto next_tok; + case VP_UNDEF: + case VP_DEFINE: + case VP_IFDEF: + case VP_IFNDEF: + case VP_ELSIF: + m_state = ps_DEFNAME; m_stateFor = tok; + goto next_tok; + case VP_ELSE: + if (m_ifdefStack.empty()) { + fileline()->v3error("`else with no matching `if\n"); + } else { + bool lastEnable = m_ifdefStack.top(); m_ifdefStack.pop(); + bool enable = !lastEnable; + UINFO(4,"Else "<<(enable?" ON":" OFF")<v3error("`endif with no matching `if\n"); + } else { + bool lastEnable = m_ifdefStack.top(); m_ifdefStack.pop(); + UINFO(4,"Endif "< V3PreProc::DEFINE_RECURSION_LEVEL_MAX) { + fileline()->v3error("Recursive `define substitution: `"+name); + goto next_tok; + } + // Substitute + if (!defExists(name)) { // Not found, return original string as-is + m_defDepth = 0; + UINFO(4,"Defref `"< not_defined"< "<unputString(out.c_str()); + goto next_tok; + } + else { // Found, with parameters + UINFO(4,"Defref `"< parameterized"<v3fatalSrc("Bad case\n"); + } + else goto next_tok; + } + case VP_ERROR: { + m_state = ps_ERRORNAME; m_stateFor = tok; + goto next_tok; + } + case VP_EOF: + if (!m_ifdefStack.empty()) { + fileline()->v3error("`ifdef not terminated at EOF\n"); + } + return tok; + case VP_SYMBOL: + case VP_STRING: + case VP_PSL: + case VP_TEXT: { + m_defDepth = 0; + if (!m_off) return tok; + else goto next_tok; + } + case VP_WHITE: // Handled at top of loop + case VP_COMMENT: // Handled at top of loop + case VP_DEFVALUE: // Handled by m_state=ps_DEFVALUE; + default: + fileline()->v3fatalSrc("Internal error: Unexpected token.\n"); + break; + } + return tok; + } +} + +string V3PreProcImp::getline() { + // Get a single line from the parse stream. Buffer unreturned text until the newline. + if (isEof()) return ""; + char* rtnp; + while (NULL==(rtnp=strchr(m_lineChars.c_str(),'\n'))) { + int tok = getToken(); + if (debug()>4) { + char buf[100000]; + strncpy(buf, yytext, yyleng); + buf[yyleng] = '\0'; + for (char* cp=buf; *cp; cp++) if (*cp=='\n') *cp='$'; + fprintf (stderr,"%d: GETFETC: %-10s: %s\n", + fileline()->lineno(), tokenName(tok), buf); + } + if (tok==VP_EOF) { + // Add a final newline, in case the user forgot the final \n. + m_lineChars.append("\n"); + } + else if (tok==VP_PSL) { + m_lineChars.append(" psl "); + } + else { + m_lineChars.append(yytext,0,yyleng); + } + } + + // Make new string with data up to the newline. + int len = rtnp-m_lineChars.c_str()+1; + string theLine(m_lineChars, 0, len); + m_lineChars = m_lineChars.erase(0,len); // Remove returned characters + UINFO(4, fileline()->lineno()<<": GETLINE: "< +#include +#include + +class V3PreProc { + // This defines a preprocessor. Functions are virtual so implementation can be hidden. + // After creating, call open(), then getline() in a loop. The class will to the rest... + +protected: + // STATE + FileLine* m_fileline; // Last token's starting point + int m_debug; // Debugging + +public: + // CONSTANTS + enum MiscConsts { + DEFINE_RECURSION_LEVEL_MAX = 50, // How many `def substitutions before an error + INCLUDE_DEPTH_MAX = 500 // How many `includes deep before an error + }; + + // ACCESSORS + // Insert given file into this point in input stream + virtual void openFile(FileLine* fileline, const string& filename)=0; + virtual string getline()=0; // Return next line/lines. (Null if done.) + virtual bool isEof() const =0; // Return true on EOF. + virtual void insertUnreadback(const string& text) = 0; + + int debug() const { return m_debug; } + void debug(int level) { m_debug = level; } + + FileLine* fileline() { return m_fileline; } // File/Line number for last getline call + + // CONTROL METHODS + // These options control how the parsing proceeds + int keepComments() { return 2; } // Return comments, 0=no, 1=yes, 2=callback + bool lineDirectives() { return true; } // Insert `line directives + bool pedantic() { return false; } // Obey standard; Don't substitute `__FILE__ and `__LINE__ + static bool optPsl(); + + // CALLBACK METHODS + // This probably will want to be overridden for given child users of this class. + virtual void comment(const string& cmt)=0; // Comment detected (if keepComments==2) + virtual void include(const string& filename)=0; // Request a include file be processed + + virtual void undef(const string& name)=0; // Remove a definition + virtual void define(FileLine* fileline, const string& name, + const string& value, const string& params="")=0; // `define without any parameters + virtual string removeDefines(const string& text)=0; // Remove defines in a text string + +protected: + // CONSTUCTORS + V3PreProc(FileLine* fl) { + m_fileline=fl; + m_debug=0; + }; +public: + static V3PreProc* createPreProc(FileLine* fileline); + virtual ~V3PreProc() {}; +}; + +#endif // Guard diff --git a/src/V3PreShell.cpp b/src/V3PreShell.cpp new file mode 100644 index 000000000..5024f9b61 --- /dev/null +++ b/src/V3PreShell.cpp @@ -0,0 +1,147 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Preprocessing wrapper +// +// 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 +#include +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3PreShell.h" +#include "V3PreProc.h" +#include "V3File.h" + +//###################################################################### + +class V3PreShellImp { +protected: + friend class V3PreShell; + + static V3PreShellImp s_preImp; + static V3PreProc* s_preprocp; + + //int debug() { return 9; } + + //--------------------------------------- + // METHODS + + void boot(char** env) { + // Create the implementation pointer + if (env) {} + if (!s_preprocp) { + FileLine* cmdfl = new FileLine("COMMAND_LINE",0); + s_preprocp = V3PreProc::createPreProc(cmdfl); + s_preprocp->debug(debug()); + // Default defines + FileLine* prefl = new FileLine("INTERNAL_VERILATOR_DEFINE",0); + s_preprocp->define(prefl,"verilator", "1"); + s_preprocp->define(prefl,"verilator3", "1"); + s_preprocp->define(prefl,"systemc_clock", "/*verilator systemc_clock*/"); + s_preprocp->define(prefl,"coverage_block_off", "/*verilator coverage_block_off*/"); + // Standards - We ignore + s_preprocp->define(prefl,"endcelldefine", ""); + s_preprocp->define(prefl,"celldefine", ""); + s_preprocp->define(prefl,"resetall", ""); + s_preprocp->define(prefl,"portcoerce", ""); + s_preprocp->define(prefl,"inline", ""); + } + } + + void preproc (FileLine* fl, const string& modname, const string& vppFilename) { + // Preprocess the given module, putting output in vppFilename + unlink(vppFilename.c_str()); + UINFONL(1," Preprocessing "<fail()) { + fl->v3error("Cannot write preprocessor output: "+vppFilename); + return; + } + + // Preprocess + preprocOpen(fl, modname, "Cannot find file containing module: "); + while (!s_preprocp->isEof()) { + string line = s_preprocp->getline(); + *osp << line; + } + + // Close + if (ofp) { + ofp->close(); + delete ofp; + osp = ofp = NULL; + } + } + + void preprocInclude (FileLine* fl, const string& modname) { + preprocOpen(fl, modname, "Cannot find include file: "); + } + + void preprocOpen (FileLine* fl, const string& modname, const string& errmsg) { + // Allow user to put `defined names on the command line instead of filenames, + // then convert them properly. + string ppmodname = s_preprocp->removeDefines (modname); + + // Open include or master file + string filename = v3Global.opt.filePath (fl, ppmodname, errmsg); + if (filename=="") return; // Not found + + UINFO(2," Reading "<openFile(fl, filename); + } + + // CONSTRUCTORS + V3PreShellImp() {} +}; + +V3PreShellImp V3PreShellImp::s_preImp; +V3PreProc* V3PreShellImp::s_preprocp = NULL; + +//###################################################################### +// Perl class functions + +void V3PreShell::boot(char** env) { + V3PreShellImp::s_preImp.boot(env); +} +void V3PreShell::preproc(FileLine* fl, const string& modname, const string& vppFilename) { + V3PreShellImp::s_preImp.preproc(fl, modname, vppFilename); +} +void V3PreShell::preprocInclude(FileLine* fl, const string& modname) { + V3PreShellImp::s_preImp.preprocInclude(fl, modname); +} +void V3PreShell::define(const string& name, const string& value) { + FileLine* prefl = new FileLine("COMMAND_LINE_DEFINE",0); + V3PreShellImp::s_preprocp->define(prefl, name,value,""); +} +void V3PreShell::undef(const string& name) { + V3PreShellImp::s_preprocp->undef(name); +} diff --git a/src/V3PreShell.h b/src/V3PreShell.h new file mode 100644 index 000000000..0cb7238df --- /dev/null +++ b/src/V3PreShell.h @@ -0,0 +1,41 @@ +// $Id$ //-*- C++ -*- +//************************************************************************* +// DESCRIPTION: Verilator: Preprocessing wrapper program +// +// 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 _V3PRESHELL_H_ +#define _V3PRESHELL_H_ 1 + +#include "config.h" +#include "V3Error.h" + +//============================================================================ + +class V3PreShell { + // Static class for calling preprocessor +public: + static void boot(char** env); + static void preproc(FileLine* fileline, const string& module, const string& vppFilename); + static void preprocInclude(FileLine* fileline, const string& module); + static string dependFiles() { return ""; } // Perl only + static void define(const string& name, const string& value); + static void undef(const string& name); +}; + +#endif // Guard diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp new file mode 100644 index 000000000..b5a4c523b --- /dev/null +++ b/src/V3Premit.cpp @@ -0,0 +1,296 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Add temporaries, such as for premit 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. +// +//************************************************************************* +// V3Premit's Transformations: +// +// Each module: +// For each wide OP, make a a temporary variable with the wide value +// For each deep expression, assign expression to temporary. +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Premit.h" +#include "V3Ast.h" + +//###################################################################### +// Premit state, as a visitor of each AstNode + +class PremitVisitor : public AstNVisitor { +private: + // NODE STATE + // AstNodeMath::user() -> bool. True if iterated already + // AstShiftL::user2() -> bool. True if converted to conditional + // AstShiftR::user2() -> bool. True if converted to conditional + + // STATE + AstModule* m_modp; // Current module + AstCFunc* m_funcp; // Current block + AstNode* m_stmtp; // Current statement + AstWhile* m_inWhilep; // Inside while loop, special statement additions + AstTraceInc* m_inTracep; // Inside while loop, special statement additions + bool m_assignLhs; // Inside assignment lhs, don't breakup extracts + + //int debug() { return 9; } + + // METHODS + bool assignNoTemp(AstNodeAssign* nodep) { + return (nodep->lhsp()->castVarRef() + && !nodep->lhsp()->castVarRef()->varp()->isSc() + && nodep->rhsp()->castConst()); + } + void checkNode(AstNode* nodep) { + // Consider adding a temp for this expression. + // We need to avoid adding temps to the following: + // ASSIGN(x, *here*) + // ASSIGN(CONST*here*, VARREF(!sc)) + // ARRAYSEL(*here*, ...) (No wides can be in any argument but first, so we don't check which arg is wide) + // ASSIGN(x, SEL*HERE*(ARRAYSEL()...) (m_assignLhs==true handles this.) + if (m_stmtp + && !nodep->user()) { // Not already done + if (nodep->isWide()) { // Else might be cell interconnect or something + if (m_assignLhs) { + } else if (nodep->backp()->castNodeAssign() + && assignNoTemp(nodep->backp()->castNodeAssign())) { + // Not much point if it's just a direct assignment to a constant + } else if (nodep->backp()->castArraySel()) { // ArraySel's are pointer refs, ignore + } else { + UINFO(4,"Cre Temp: "<varNumGetInc())); + AstVar* varp = new AstVar (nodep->fileline(), AstVarType::STMTTEMP, newvarname, + new AstRange(nodep->fileline(), nodep->widthMin()-1, 0)); + m_funcp->addInitsp(varp); + return varp; + } + + void insertBeforeStmt(AstNode* newp) { + // Insert newp before m_stmtp + if (m_inWhilep) { + // Statements that are needed for the 'condition' in a while actually have to + // be put before & after the loop, since we can't do any statements in a while's (cond). + m_inWhilep->addPrecondsp(newp); + } else if (m_inTracep) { + m_inTracep->addPrecondsp(newp); + } else { + AstNRelinker linker; + m_stmtp->unlinkFrBack(&linker); + newp->addNext(m_stmtp); + linker.relink(newp); + } + } + + void createDeepTemp(AstNode* nodep) { + if (debug()>8) nodep->dumpTree(cout,"deepin:"); + + AstNRelinker linker; + nodep->unlinkFrBack(&linker); + + AstVar* varp = getBlockTemp(nodep); + // Replace node tree with reference to var + AstVarRef* newp = new AstVarRef (nodep->fileline(), varp, false); + linker.relink(newp); + // Put assignment before the referencing statement + AstAssign* assp = new AstAssign (nodep->fileline(), + new AstVarRef(nodep->fileline(), varp, true), + nodep); + insertBeforeStmt(assp); + if (debug()>8) assp->dumpTree(cout,"deepou:"); + nodep->user(true); // Don't add another assignment + } + + // VISITORS + virtual void visit(AstModule* nodep, AstNUser*) { + UINFO(4," MOD "<iterateChildren(*this); + } + virtual void visit(AstCFunc* nodep, AstNUser*) { + m_funcp = nodep; + nodep->iterateChildren(*this); + } + void startStatement(AstNode* nodep) { + m_assignLhs = false; + if (m_funcp) m_stmtp = nodep; + } + virtual void visit(AstWhile* nodep, AstNUser*) { + UINFO(4," WHILE "<precondsp()->iterateAndNext(*this); + startStatement(nodep); + m_inWhilep = nodep; + nodep->condp()->iterateAndNext(*this); + m_inWhilep = NULL; + startStatement(nodep); + nodep->bodysp()->iterateAndNext(*this); + m_stmtp = NULL; + } + virtual void visit(AstNodeAssign* nodep, AstNUser*) { + startStatement(nodep); + nodep->rhsp()->iterateAndNext(*this); + m_assignLhs = true; + nodep->lhsp()->iterateAndNext(*this); + m_assignLhs = false; + m_stmtp = NULL; + } + virtual void visit(AstNodeStmt* nodep, AstNUser*) { + UINFO(4," STMT "<iterateChildren(*this); + m_stmtp = NULL; + } + virtual void visit(AstTraceInc* nodep, AstNUser*) { + startStatement(nodep); + m_inTracep = nodep; + nodep->iterateChildren(*this); + m_inTracep = NULL; + m_stmtp = NULL; + } + void visitShift (AstNodeBiop* nodep) { + // Shifts of > 32/64 bits in C++ will wrap-around and generate non-0s + if (!nodep->user2()) { + UINFO(4," ShiftFix "<user2(true); + if (nodep->widthMin()<=64 // Else we'll use large operators which work right + // C operator's width must be < maximum shift which is based on Verilog width + && nodep->width() < (1LL<rhsp()->widthMin())) { + AstNRelinker replaceHandle; + nodep->unlinkFrBack(&replaceHandle); + AstNode* constzerop; + if (nodep->signedFlavor()) { + // Then over shifting gives the sign bit, not all zeros + // Note *NOT* clean output -- just like normal shift! + // Create equivalent of VL_SIGNONES_(node_width) + constzerop = new AstUnaryMin (nodep->fileline(), + new AstShiftR(nodep->fileline(), + nodep->lhsp()->cloneTree(false), + new AstConst(nodep->fileline(), + nodep->widthMin()-1), + nodep->width())); + } else { + V3Number zeronum (nodep->fileline(), nodep->width(), 0); + constzerop = new AstConst(nodep->fileline(), zeronum); + } + constzerop->widthFrom (nodep); // unsigned + V3Number widthnum (nodep->fileline(), nodep->rhsp()->widthMin(), nodep->width()-1); + AstNode* constwidthp = new AstConst(nodep->fileline(), widthnum); + constwidthp->widthFrom (nodep->rhsp()); // unsigned + AstCond* newp = + new AstCond (nodep->fileline(), + new AstLte (nodep->fileline(), + nodep->rhsp()->cloneTree(false), + constwidthp), + nodep, + constzerop); + newp->widthSignedFrom(nodep); + replaceHandle.relink(newp); + } + } + nodep->iterateChildren(*this); checkNode(nodep); + } + virtual void visit(AstShiftL* nodep, AstNUser*) { + visitShift(nodep); + } + virtual void visit(AstShiftR* nodep, AstNUser*) { + visitShift(nodep); + } + virtual void visit(AstShiftRS* nodep, AstNUser*) { + visitShift(nodep); + } + // Operators + virtual void visit(AstNodeTermop* nodep, AstNUser*) { + nodep->iterateChildren(*this); checkNode(nodep); } + virtual void visit(AstNodeUniop* nodep, AstNUser*) { + nodep->iterateChildren(*this); checkNode(nodep); } + virtual void visit(AstNodeBiop* nodep, AstNUser*) { + nodep->iterateChildren(*this); checkNode(nodep); } + virtual void visit(AstSel* nodep, AstNUser*) { + nodep->fromp()->iterateAndNext(*this); + { // Only the 'from' is part of the assignment LHS + bool prevAssign = m_assignLhs; + m_assignLhs = false; + nodep->lsbp()->iterateAndNext(*this); + nodep->widthp()->iterateAndNext(*this); + m_assignLhs = prevAssign; + } + checkNode(nodep); } + virtual void visit(AstConst* nodep, AstNUser*) { + nodep->iterateChildren(*this); checkNode(nodep); } + virtual void visit(AstNodeCond* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (nodep->expr1p()->isWide() + && !nodep->condp()->castConst() + && !nodep->condp()->castVarRef()) { + // We're going to need the expression several times in the expanded code, + // so might as well make it a common expression + createDeepTemp(nodep->condp()); + } + checkNode(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 + PremitVisitor() { + m_modp = NULL; + m_funcp = NULL; + m_stmtp = NULL; + m_inWhilep = NULL; + m_inTracep = NULL; + } + virtual ~PremitVisitor() {} + void main(AstNode* nodep) { + AstNode::userClearTree(); // userp() used on entire tree + AstNode::user2ClearTree(); // user2p() used on entire tree + nodep->accept(*this); + } +}; + +//---------------------------------------------------------------------- +// Top loop + +//###################################################################### +// Premit class functions + +void V3Premit::premitAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include + +#include "V3Error.h" +#include "V3Global.h" +#include "V3Ast.h" +#include "V3File.h" +#include "V3Read.h" +#include "V3PreShell.h" + +//====================================================================== +// Build in LEX script + +#define yyFlexLexer V3LexerBase +#include "V3Lexer.yy.cpp" +#undef yyFlexLexer + +//YYSTYPE yylval; + +//====================================================================== +// Globals + +V3Read* V3Read::s_readp = NULL; + +extern bool yyparse(); +extern int yydebug; + +//###################################################################### +// Lex-derived class + +/// Override the base lexer class so we can add some access functions +class V3Lexer : public V3LexerBase { +public: + // CONSTRUCTORS + V3Lexer(std::istream* arg_yyin) : V3LexerBase(arg_yyin) {} + ~V3Lexer() {} + // METHODS + void stateExitPsl() { + if (YY_START != PSL) yyerror("Internal error: Exiting PSL state when not in PSL state"); + BEGIN VLG; + } + void statePushVlg() { + yy_push_state(VLG); + } + void statePop() { + yy_pop_state(); + } +}; +void V3Read::stateExitPsl() { s_readp->m_lexerp->stateExitPsl(); } +void V3Read::statePushVlg() { s_readp->m_lexerp->stateExitPsl(); } +void V3Read::statePop() { s_readp->m_lexerp->statePop(); } + +//###################################################################### +// Read class functions + +void V3Read::readFile(FileLine* fileline, const string& modfilename, bool inLibrary) { + string modname = V3Options::filenameNonExt(modfilename); + + UINFO(2,__FUNCTION__<<": "<fail()) { + m_fileline->v3fatal("Module "<warnResetDefault(); // Reenable warnings on each file + if (m_lexerp) delete m_lexerp; // Restart from clean slate. + m_lexerp = new V3Lexer(fsp); + // if (debug()) { m_lexerp->set_debug(~0); } + // if (debug()) yydebug = 1; + UINFO(4,"Lexing Done "<close(); delete fsp; fsp = NULL; +} + +//====================================================================== +// Lex accessors + +bool V3Read::optPsl() { + return v3Global.opt.psl(); +} + +//====================================================================== +// Lex internal functions + +int V3Read::yylexThis() { + int token = m_lexerp->yylex(); + UINFO(5,m_fileline<<" TOKEN="< + +class AstNetlist; +class V3Lexer; +class V3Number; +class AstNode; + +//============================================================================ + +class V3Read { + AstNetlist* m_rootp; // Root of the design + V3Lexer* m_lexerp; // Current FlexLexer + static V3Read* s_readp; // Current THIS, bison() isn't class based + FileLine* m_fileline; // Filename/linenumber currently active + bool m_inLibrary; // Currently reading a library vs. regular file + deque m_stringps; // Created strings for later cleanup + deque m_numberps; // Created numbers for later cleanup + //int debug() { return 9; } + +protected: + // Functions called by lex rules: + friend class V3Lexer; + friend class V3LexerBase; + friend class FileLine; + int yylexThis(); + static bool optPsl(); + static void ppline (const char* text); + static void incLineno() { s_readp->fileline()->incLineno(); } + static void verilatorCmtLint(const char* text, bool on); + static void verilatorCmtBad(const char* text); + +public: // But for internal use only + static string* newString(const string& text) { + // Allocate a string, remembering it so we can reclaim storage at lex end + string* strp = new string (text); + s_readp->m_stringps.push_back(strp); + return strp; + } + static string* newString(const char* text) { + // Allocate a string, remembering it so we can reclaim storage at lex end + string* strp = new string (text); + s_readp->m_stringps.push_back(strp); + return strp; + } + static string* newString(const char* text, int length) { + string* strp = new string (text, length); + s_readp->m_stringps.push_back(strp); + return strp; + } + static V3Number* newNumber(FileLine* fl, const char* text) { + V3Number* nump = new V3Number (fl, text); + s_readp->m_numberps.push_back(nump); + return nump; + } + + // Return next token, for bison, since bison isn't class based, use a global THIS + static int yylex() { return s_readp->yylexThis(); }; + static FileLine* fileline() { return s_readp->m_fileline; } + static AstNetlist* rootp() { return s_readp->m_rootp; } + static FileLine* copyOrSameFileLine() { return s_readp->fileline()->copyOrSameFileLine(); } + static bool inLibrary() { return s_readp->m_inLibrary; } + static void stateExitPsl(); // Parser -> lexer communication + static void statePushVlg(); // Parser -> lexer communication + static void statePop(); // Parser -> lexer communication + +public: + // CREATORS + V3Read(AstNetlist* rootp) { + m_rootp = rootp; m_lexerp = NULL; + m_inLibrary = false; + } + ~V3Read() { + for (deque::iterator it = m_stringps.begin(); it != m_stringps.end(); ++it) { + delete (*it); + } + m_stringps.clear(); + for (deque::iterator it = m_numberps.begin(); it != m_numberps.end(); ++it) { + delete (*it); + } + m_numberps.clear(); + } + + // METHODS + // Preprocess and read the Verilog file specified into the netlist database + void readFile(FileLine* fileline, const string& modname, bool inLibrary); + +private: + void lexFile(const string& vppfilename, const string& modname); +}; + +#endif // Guard diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp new file mode 100644 index 000000000..9fbf0f0c5 --- /dev/null +++ b/src/V3Scope.cpp @@ -0,0 +1,294 @@ +// $Id$ +//************************************************************************* +// 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. +// +//************************************************************************* +// V3Scope's Transformations: +// +// For every CELL that references this module, create a +// SCOPE +// {all blocked statements} +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Scope.h" +#include "V3Ast.h" + +//###################################################################### +// Scope class functions + +class ScopeVisitor : public AstNVisitor { +private: + // NODE STATE + // AstVar::userp -> AstVarScope replacement for this variable + // AstVarRef::user2p -> bool. True indicates already processed + + // STATE, inside processing a single module + AstModule* m_modp; // Current module + AstScope* m_scopep; // Current scope we are building + // STATE, for passing down one level of hierarchy (may need save/restore) + AstCell* m_aboveCellp; // Cell that instantiates this module + AstScope* m_aboveScopep; // Scope that instantiates this scope + + //int debug() { return 9; } + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + AstModule* modp = nodep->topModulep(); + if (!modp) { nodep->v3error("No root module specified"); return; } + // Operate starting at the top of the hierarchy + AstNode::user2ClearTree(); + m_aboveCellp = NULL; + m_aboveScopep = NULL; + modp->accept(*this); + } + virtual void visit(AstModule* nodep, AstNUser*) { + // Create required blocks and add to module + string scopename = (!m_aboveScopep ? "TOP" + : (m_aboveScopep->name()+"."+m_aboveCellp->name())); + + UINFO(4," MOD AT "<fileline(), + nodep, scopename, m_aboveScopep, m_aboveCellp); + + // Now for each child cell, iterate the module this cell points to + for (AstNode* cellnextp = nodep->stmtsp(); cellnextp; cellnextp=cellnextp->nextp()) { + if (AstCell* cellp = cellnextp->castCell()) { + AstScope* oldScopep = m_scopep; + AstCell* oldAbCellp = m_aboveCellp; + AstScope* oldAbScopep = m_aboveScopep; + { + m_aboveCellp = cellp; + m_aboveScopep = m_scopep; + AstModule* modp = cellp->modp(); + if (!modp) cellp->v3fatalSrc("Unlinked mod"); + modp->accept(*this); // Recursive call to visit(AstModule) + } + // Done, restore vars + m_scopep = oldScopep; + m_aboveCellp = oldAbCellp; + m_aboveScopep = oldAbScopep; + } + } + + // Create scope for the current usage of this module + UINFO(4," back AT "<isTop()) { + AstTopScope* topscp = new AstTopScope(nodep->fileline(), m_scopep); + m_modp->addStmtp(topscp); + } else { + m_modp->addStmtp(m_scopep); + } + + // Copy blocks into this scope + // If this is the first usage of the block ever, we can simply move the reference + nodep->iterateChildren(*this); + + // ***Note m_scopep is passed back to the caller of the routine (above) + } + virtual void visit(AstActive* nodep, AstNUser*) { + nodep->v3fatalSrc("Actives now made after scoping"); + } + virtual void visit(AstInitial* nodep, AstNUser*) { + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false)->castInitial(); + m_scopep->addActivep(clonep); + clonep->iterateChildren(*this); // We iterate under the *clone* + } + virtual void visit(AstFinal* nodep, AstNUser*) { + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false)->castFinal(); + m_scopep->addActivep(clonep); + clonep->iterateChildren(*this); // We iterate under the *clone* + } + virtual void visit(AstAssignAlias* nodep, AstNUser*) { + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false); + m_scopep->addActivep(clonep); + clonep->iterateChildren(*this); // We iterate under the *clone* + } + virtual void visit(AstAssignW* nodep, AstNUser*) { + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false); + m_scopep->addActivep(clonep); + clonep->iterateChildren(*this); // We iterate under the *clone* + } + virtual void visit(AstAlways* nodep, AstNUser*) { + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false)->castAlways(); + m_scopep->addActivep(clonep); + clonep->iterateChildren(*this); // We iterate under the *clone* + } + virtual void visit(AstCFunc* nodep, AstNUser*) { + // Add to list of blocks under this scope + UINFO(4," CFUNC "<cloneTree(false)->castCFunc(); + clonep->scopep(m_scopep); + m_scopep->addActivep(clonep); + // We iterate under the *clone* + clonep->iterateChildren(*this); + } + virtual void visit(AstNodeFTask* nodep, AstNUser*) { + // Add to list of blocks under this scope + UINFO(4," FTASK "<cloneTree(false)->castNodeFTask(); + m_scopep->addActivep(clonep); + // We iterate under the *clone* + clonep->iterateChildren(*this); + } + virtual void visit(AstVar* nodep, AstNUser*) { + // Make new scope variable + if (!nodep->userp()) { + AstVarScope* varscp = new AstVarScope(nodep->fileline(), m_scopep, nodep); + UINFO(6," New scope "<userp(varscp); + m_scopep->addVarp(varscp); + } + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + // VarRef needs to point to VarScope + // Make sure variable has made userp. + if (!nodep->user2()) { + nodep->varp()->accept(*this); + AstVarScope* varscp = (AstVarScope*)nodep->varp()->userp(); + if (!varscp) nodep->v3fatalSrc("Can't locate varref scope"); + nodep->varScopep(varscp); + } + } + virtual void visit(AstVarXRef* nodep, AstNUser*) { + // The crossrefs are dealt with in V3LinkDot + nodep->varp(NULL); + } + virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { + // The crossrefs are dealt with in V3LinkDot + nodep->taskp(NULL); + nodep->iterateChildren(*this); + } + virtual void visit(AstScope* nodep, AstNUser*) { + // Scope that was made by this module for different cell; + // Want to ignore blocks under it, so just do nothing + } + //-------------------- + // Default + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + ScopeVisitor(AstNode* nodep) { + m_aboveCellp = NULL; + m_aboveScopep = NULL; + m_modp = NULL; + m_scopep = NULL; + // + nodep->accept(*this); + } + virtual ~ScopeVisitor() {} +}; + +//###################################################################### +// Scope cleanup -- remove unused activates + +class ScopeCleanupVisitor : public AstNVisitor { +private: + // STATE + AstScope* m_scopep; // Current scope we are building + + //int debug() { return 9; } + + // METHODS + // VISITORS + virtual void visit(AstScope* nodep, AstNUser*) { + // Want to ignore blocks under it + m_scopep = nodep; + nodep->iterateChildren(*this); + m_scopep = NULL; + } + + virtual void movedDeleteOrIterate(AstNode* nodep) { + if (m_scopep) { + // The new block; repair varrefs + nodep->iterateChildren(*this); + } else { + // A block that was just moved under a scope, Kill it. + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } + } + + virtual void visit(AstInitial* nodep, AstNUser*) { + movedDeleteOrIterate(nodep); + } + virtual void visit(AstFinal* nodep, AstNUser*) { + movedDeleteOrIterate(nodep); + } + virtual void visit(AstAssignAlias* nodep, AstNUser*) { + movedDeleteOrIterate(nodep); + } + virtual void visit(AstAssignW* nodep, AstNUser*) { + movedDeleteOrIterate(nodep); + } + virtual void visit(AstAlways* nodep, AstNUser*) { + movedDeleteOrIterate(nodep); + } + virtual void visit(AstNodeFTask* nodep, AstNUser*) { + movedDeleteOrIterate(nodep); + } + virtual void visit(AstCFunc* nodep, AstNUser*) { + movedDeleteOrIterate(nodep); + } + + //-------------------- + // Default + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + ScopeCleanupVisitor(AstNode* nodep) { + m_scopep = NULL; + nodep->accept(*this); + } + virtual ~ScopeCleanupVisitor() {} +}; + +//###################################################################### +// Scope class functions + +void V3Scope::scopeAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Ast.h" + +//###################################################################### +// Collect SenTrees under the entire scope +// And provide functions to find/add a new one + +class SenTreeFinder : public AstNVisitor { +private: + // STATE + AstTopScope* m_topscopep; // Top scope to add statement to + vector m_treesp; // List of sensitive blocks, for folding + //int debug() { return 9; } + // VISITORS + virtual void visit(AstModule* nodep, AstNUser*) { + // Only do the top + if (nodep->isTop()) { + nodep->iterateChildren(*this); + } + } + virtual void visit(AstTopScope* nodep, AstNUser*) { + m_topscopep = nodep; + nodep->iterateChildren(*this); + // Don't clear scopep, the namer persists beyond this visit + } + virtual void visit(AstScope* nodep, AstNUser*) { + // But no SenTrees under TopScope's scope + } + // Memorize existing block names + virtual void visit(AstActive* nodep, AstNUser*) { + // Don't grab SenTrees under Actives, only those that are global (under Scope directly) + nodep->iterateChildren(*this); + } + virtual void visit(AstSenTree* nodep, AstNUser*) { + m_treesp.push_back(nodep); + } + // Empty visitors, speed things up + virtual void visit(AstNodeStmt* nodep, AstNUser*) { } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + // METHODS +public: + void clear() { + m_topscopep = NULL; + m_treesp.clear(); + } + AstSenTree* getSenTree(FileLine* fl, AstSenTree* sensesp) { + // Return a global sentree that matches given sense list. + // Not the fastest, but there tend to be few clocks + AstSenTree* treep = NULL; + //sensesp->dumpTree(cout," Lookingfor: "); + for (vector::iterator it = m_treesp.begin(); it!=m_treesp.end(); ++it) { + treep = *it; + if (treep) { // Not deleted + if (treep->sameTree(sensesp)) { + UINFO(8," Found SBLOCK "<cloneTree(false)->castSenTree(); + m_topscopep->addStmtsp(treep); + UINFO(8," New SENTREE "<accept(*this); + } +}; + +#endif // Guard diff --git a/src/V3Signed.cpp b/src/V3Signed.cpp new file mode 100644 index 000000000..83cab04b8 --- /dev/null +++ b/src/V3Signed.cpp @@ -0,0 +1,395 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Signed/unsigned resolution +// +// 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. +// +//************************************************************************* +// Signedness depends on: +// Decimal numbers are signed +// Based numbers are unsigned unless 's' prefix +// Comparison results are unsigned +// Bit&Part selects are unsigned, even if whole +// Concatenates are unsigned +// Ignore signedness of self-determined: +// shift rhs, ** rhs, x?: lhs, concat and replicate members +// Else, if any operand unsigned, output unsigned +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Signed.h" +#include "V3Ast.h" + +//###################################################################### +// Signed class functions + +class SignedVisitor : public AstNVisitor { +private: + // NODE STATE/TYPES + // STATE + int m_taskDepth; // Recursion check + bool m_paramsOnly; // Computing parameter value; limit operation + + // METHODS - special type detection + bool backRequiresUnsigned(AstNode* nodep) { + // The spec doesn't state this, but if you have an array select where the selection + // index is NOT wide enough, you do not sign extend, but always zero extend. + return (nodep->castArraySel() + || nodep->castSel()); + } + + // VISITORS + //======== + // Signed: Output explicit by user, Lhs either + virtual void visit(AstSigned* nodep, AstNUser*) { signed_Os_Ix(nodep); } + virtual void visit(AstUnsigned* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + + //======== + // Signed: Output unsigned, Operands either + virtual void visit(AstArraySel* nodep, AstNUser*) { signed_Ou_Ix(nodep); } //See backRequiresUnsigned + virtual void visit(AstSel* nodep, AstNUser*) { signed_Ou_Ix(nodep); } //See backRequiresUnsigned + virtual void visit(AstAttrOf* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstCountOnes* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstPslBool* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstTime* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + // + virtual void visit(AstRedAnd* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstRedOr* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstRedXnor* nodep, AstNUser*){ signed_Ou_Ix(nodep); } + virtual void visit(AstRedXor* nodep,AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstIsUnknown* nodep,AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstOneHot* nodep,AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstOneHot0* nodep,AstNUser*) { signed_Ou_Ix(nodep); } + // + virtual void visit(AstConcat* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstReplicate* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstRange* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + // ... One presumes these are unsigned out, though the spec doesn't say + virtual void visit(AstLogNot* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstLogAnd* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstLogOr* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstLogIf* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstLogIff* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + // ... These shouldn't matter, just make unsigned + virtual void visit(AstUCFunc* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstText* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + // ... These comparisons don't care about inbound types + // ... (Though they should match. We don't check.) + virtual void visit(AstEq* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstEqCase* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstNeq* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstNeqCase* nodep, AstNUser*){ signed_Ou_Ix(nodep); } + + //======= + // Signed: Output signed iff LHS signed; unary operator + virtual void visit(AstNot* nodep, AstNUser*) { signed_Olhs(nodep); } + virtual void visit(AstUnaryMin* nodep, AstNUser*) { signed_Olhs(nodep); } + virtual void visit(AstShiftL* nodep, AstNUser*) { signed_Olhs(nodep); } + virtual void visit(AstShiftR* nodep, AstNUser*) { signed_Olhs(nodep); } + + //======= + // Signed: Output signed iff LHS & RHS signed; binary operator + virtual void visit(AstAnd* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } + virtual void visit(AstOr* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } + virtual void visit(AstXnor* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } + virtual void visit(AstXor* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } + virtual void visit(AstSub* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } + virtual void visit(AstAdd* nodep, AstNUser*) { signed_OlhsAndRhs(nodep); } + + //======= + // Signed: Output signed iff RHS & THS signed + virtual void visit(AstNodeCond* nodep, AstNUser*) { signed_OrhsAndThs(nodep); } + + //======= + // These have proper signedness set when they were created. + virtual void visit(AstFunc* nodep, AstNUser*) { nodep->iterateChildren(*this); } + virtual void visit(AstVar* nodep, AstNUser*) { nodep->iterateChildren(*this); } + + // Inherit from others + virtual void visit(AstNodeVarRef* nodep, AstNUser*) { + nodep->varp()->iterate(*this); + nodep->signedFrom(nodep->varp()); + } + virtual void visit(AstFuncRef* nodep, AstNUser* vup) { + visit(nodep->castNodeFTaskRef(), vup); // Deal with as if was task + nodep->signedFrom(nodep->taskp()); + } + virtual void visit(AstConst* nodep, AstNUser*) { + // The node got setup with the signed state of the node. + // However a later operation may have changed the node->signed w/o changing + // the number's sign. So we don't: nodep->isSigned(nodep->num().isSigned()); + } + virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { + // Fortunately, the input variables to the task keep whatever sign they are given + // And we already did the function's output signedness above in visit(AstFuncRef, + // so, it's just + nodep->iterateChildren(*this); + } + virtual void visit(AstPin* nodep, AstNUser*) { + // Same as above taskref argument. + nodep->iterateChildren(*this); + } + + // VISITORS - Special + virtual void visit(AstDisplay* nodep, AstNUser*) { + nodep->iterateChildren(*this); + // + UINFO(9," Display in "<text()<exprsp(); + for (const char* inp = nodep->text().c_str(); *inp; inp++) { + char ch = *inp; // Breaks with iterators... + if (!inPct && ch=='%') { + inPct = true; + } else if (inPct && isdigit(ch)) { + } else if (inPct) { + inPct = false; + switch (tolower(ch)) { + case '%': break; // %% - just output a % + case 'm': break; // %m - auto insert "name" + case 'd': { // Convert decimal to either 'd' or 'u' + if (argp && argp->isSigned()) { // Convert it + ch = '~'; + } + if (argp) argp=argp->nextp(); + break; + } + default: // Most operators, just move to next argument + if (argp) argp=argp->nextp(); + break; + } // switch + } + dispout += ch; + } + nodep->text(dispout); + UINFO(9," Display out "<text()<isSigned() && !backRequiresUnsigned(nodep->backp())) { + replaceWithSignedVersion(nodep, new AstExtendS (nodep->fileline(), nodep->lhsp()->unlinkFrBack())); nodep=NULL; + } + } + virtual void visit(AstExtendS* nodep, AstNUser*) { + signed_Olhs(nodep); + if (!(nodep->isSigned() && !backRequiresUnsigned(nodep->backp()))) { + replaceWithSignedVersion(nodep, new AstExtend (nodep->fileline(), nodep->lhsp()->unlinkFrBack())); nodep=NULL; + } + } + + // Biop + virtual void visit(AstPow* nodep, AstNUser*) { + // Pow is special, output sign only depends on LHS sign + signed_Olhs(nodep); + if (nodep->isSigned()) { + replaceWithSignedVersion(nodep, new AstPowS (nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack())); nodep=NULL; + } + } + virtual void visit(AstPowS* nodep, AstNUser*) { + // Pow is special, output sign only depends on LHS sign + signed_Olhs(nodep); + if (!nodep->isSigned()) { + replaceWithSignedVersion(nodep, new AstPow (nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack())); nodep=NULL; + } + } + + // These have different node types, as they operate differently + // Must add to case statement below, + virtual void visit(AstGt* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } + virtual void visit(AstGtS* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } + virtual void visit(AstGte* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } + virtual void visit(AstGteS* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } + virtual void visit(AstLt* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } + virtual void visit(AstLtS* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } + virtual void visit(AstLte* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } + virtual void visit(AstLteS* nodep, AstNUser*) { checkReplace_Ou_FlavLhsAndRhs(nodep); } + // Need replacements; output matches input sign + virtual void visit(AstDiv* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } + virtual void visit(AstDivS* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } + virtual void visit(AstModDiv* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } + virtual void visit(AstModDivS* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } + virtual void visit(AstMul* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } + virtual void visit(AstMulS* nodep, AstNUser*) { checkReplace_OlhsAndRhs(nodep); } + // ShiftRS converts to ShiftR, but not vice-versa + virtual void visit(AstShiftRS* nodep, AstNUser*) { checkReplace_Olhs(nodep); } + + void checkReplace_Ou_FlavLhsAndRhs(AstNodeBiop* nodep) { + // For compares, the output of the comparison is unsigned. + // However, we need the appropriate type of compare selected by RHS & LHS + signed_Ou_Ix(nodep); + checkReplace(nodep, nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); + } + void checkReplace_Olhs(AstNodeBiop* nodep) { + signed_Olhs(nodep); + checkReplace(nodep, nodep->isSigned()); + } + void checkReplace_OlhsAndRhs(AstNodeBiop* nodep) { + signed_OlhsAndRhs(nodep); + checkReplace(nodep, nodep->isSigned()); + } + + void checkReplace(AstNodeBiop* nodep, bool signedFlavorNeeded) { + if (signedFlavorNeeded != nodep->signedFlavor()) { + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* newp = NULL; + // Given a signed/unsigned node type, create the opposite type + switch (nodep->type()) { + case AstType::GT: newp = new AstGtS (nodep->fileline(), lhsp, rhsp); break; + case AstType::GTS: newp = new AstGt (nodep->fileline(), lhsp, rhsp); break; + case AstType::GTE: newp = new AstGteS (nodep->fileline(), lhsp, rhsp); break; + case AstType::GTES: newp = new AstGte (nodep->fileline(), lhsp, rhsp); break; + case AstType::LT: newp = new AstLtS (nodep->fileline(), lhsp, rhsp); break; + case AstType::LTS: newp = new AstLt (nodep->fileline(), lhsp, rhsp); break; + case AstType::LTE: newp = new AstLteS (nodep->fileline(), lhsp, rhsp); break; + case AstType::LTES: newp = new AstLte (nodep->fileline(), lhsp, rhsp); break; + case AstType::DIV: newp = new AstDivS (nodep->fileline(), lhsp, rhsp); break; + case AstType::DIVS: newp = new AstDiv (nodep->fileline(), lhsp, rhsp); break; + case AstType::MODDIV: newp = new AstModDivS(nodep->fileline(), lhsp, rhsp); break; + case AstType::MODDIVS: newp = new AstModDiv (nodep->fileline(), lhsp, rhsp); break; + case AstType::MUL: newp = new AstMulS (nodep->fileline(), lhsp, rhsp); break; + case AstType::MULS: newp = new AstMul (nodep->fileline(), lhsp, rhsp); break; + case AstType::SHIFTRS: newp = new AstShiftR (nodep->fileline(), lhsp, rhsp); break; + default: + nodep->v3fatalSrc("Node needs sign change, but bad case: "<v3fatalSrc("Visit function missing? Signedness unknown for this node: "<iterateChildren(*this); + } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + + // COMMON SCHEMES + // Signed: Output signed, Lhs/Rhs/etc either + void signed_Os_Ix(AstNode* nodep) { + nodep->iterateChildren(*this); + nodep->isSigned(true); + } + // Signed: Output unsigned, Lhs/Rhs/etx either + void signed_Ou_Ix(AstNode* nodep) { + nodep->iterateChildren(*this); + nodep->isSigned(false); + } + // Signed: Output signed iff LHS signed; unary operator + void signed_Olhs(AstNodeUniop* nodep) { + nodep->iterateChildren(*this); + nodep->isSigned(nodep->lhsp()->isSigned()); + } + // Signed: Output signed iff LHS signed; binary operator + void signed_Olhs(AstNodeBiop* nodep) { + nodep->iterateChildren(*this); + nodep->isSigned(nodep->lhsp()->isSigned()); + } + // Signed: Output signed iff LHS & RHS signed; binary operator + void signed_OlhsAndRhs(AstNodeBiop* nodep) { + nodep->iterateChildren(*this); + nodep->isSigned(nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); + } + // Signed: Output signed iff RHS & THS signed + void signed_OrhsAndThs(AstNodeTriop* nodep) { + nodep->iterateChildren(*this); + nodep->isSigned(nodep->rhsp()->isSigned() && nodep->thsp()->isSigned()); + } + void replaceWithSignedVersion(AstNode* nodep, AstNode* newp) { + UINFO(6," Replace "<replaceWith(newp); + newp->widthSignedFrom(nodep); + pushDeletep(nodep); nodep=NULL; + } + +public: + // CONSTRUCTORS + SignedVisitor(AstNode* nodep, bool paramsOnly) { + m_paramsOnly = paramsOnly; + m_taskDepth = 0; + nodep->accept(*this); + } + virtual ~SignedVisitor() {} +}; + +//###################################################################### +// Signed class functions +/// Remove all $signed, $unsigned, we're done with them. + +class SignedRemoveVisitor : public AstNVisitor { +private: + // METHODS + void supportCheck(AstNode* nodep) { +#ifdef VL_UNSIGNED + if (nodep->isSigned()) nodep->v3error("Unsupported: signed numbers"); +#endif + } + // VISITORS + virtual void visit(AstSigned* nodep, AstNUser*) { + replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()); nodep=NULL; + } + virtual void visit(AstUnsigned* nodep, AstNUser*) { + replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()); nodep=NULL; + } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + supportCheck(nodep); + } + void replaceWithSignedVersion(AstNode* nodep, AstNode* newp) { + UINFO(6," Replace "<replaceWith(newp); + newp->widthSignedFrom(nodep); + supportCheck(nodep); + pushDeletep(nodep); nodep=NULL; + } +public: + // CONSTRUCTORS + SignedRemoveVisitor(AstNode* nodep) { + nodep->accept(*this); + } + virtual ~SignedRemoveVisitor() {} +}; + +//###################################################################### +// Top Signed class + +void V3Signed::signedAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< v3Dly --> X2 --> v2 -brk-> v2Dly -> X1 -> v1 +// Likewise on each "upper" statement vertex +// v3Dly & v2Dly -> S3 -> v1 & v2 +// v1 -brk-> v1Dly -> S2 -> v0 +// v1Dly -> S1 -> {empty} +// Multiple assignments to the same variable must remain in order +// +// Also vars must not be "public" and we also scoreboard nodep->isSplittable() +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Split.h" +#include "V3Stats.h" +#include "V3Ast.h" +#include "V3Graph.h" + +//###################################################################### +// Support classes + +class SplitPliVertex : public V3GraphVertex { +public: + SplitPliVertex(V3Graph* graphp) + : V3GraphVertex(graphp) {} + virtual ~SplitPliVertex() {} + virtual string name() const { return "*PLI*"; } + virtual string dotColor() const { return "green"; } +}; + +class SplitNodeVertex : public V3GraphVertex { + AstNode* m_nodep; +protected: + SplitNodeVertex(V3Graph* graphp, AstNode* nodep) + : V3GraphVertex(graphp), m_nodep(nodep) {} + virtual ~SplitNodeVertex() {} + // Accessors + virtual string name() const { + if (m_nodep->name() == "") { + return cvtToStr((void*)m_nodep); + } else { + return m_nodep->name(); + } + } +}; + +class SplitLogicVertex : public SplitNodeVertex { + uint32_t m_splitColor; // Copied from color() when determined +public: + SplitLogicVertex(V3Graph* graphp, AstNode* nodep) + : SplitNodeVertex(graphp,nodep) {} + void splitColor(uint32_t flag) { m_splitColor=flag; } + uint32_t splitColor() const { return m_splitColor; } + virtual ~SplitLogicVertex() {} + virtual string dotColor() const { return "yellow"; } +}; + +class SplitVarStdVertex : public SplitNodeVertex { +public: + SplitVarStdVertex(V3Graph* graphp, AstNode* nodep) + : SplitNodeVertex(graphp,nodep) {} + virtual ~SplitVarStdVertex() {} + virtual string dotColor() const { return "skyblue"; } +}; + +class SplitVarPostVertex : public SplitNodeVertex { +public: + SplitVarPostVertex(V3Graph* graphp, AstNode* nodep) + : SplitNodeVertex(graphp,nodep) {} + virtual ~SplitVarPostVertex() {} + virtual string name() const { return (string)"POST "+SplitNodeVertex::name(); } + virtual string dotColor() const { return "CadetBlue"; } +}; + +//###################################################################### +// Edge types + +class SplitEdge : public V3GraphEdge { + uint32_t m_ignoreInStep; // Step number that if set to, causes this edge to be ignored + static uint32_t s_stepNum; // Global step number +protected: + enum { WEIGHT_NORMAL=10 }; + SplitEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, + int weight, bool cutable=CUTABLE) + : V3GraphEdge(graphp, fromp, top, weight, cutable) + ,m_ignoreInStep(0) {} + virtual ~SplitEdge() {} +public: + // Iterator for graph functions + static void incrementStep() { ++s_stepNum; } + bool ignoreThisStep() const { return m_ignoreInStep == s_stepNum; } + void setIgnoreThisStep() { m_ignoreInStep = s_stepNum; } + virtual bool followScoreboard() const = 0; + static bool followScoreboard(const V3GraphEdge* edgep) { + const SplitEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type"); + if (oedgep->ignoreThisStep()) return false; + return oedgep->followScoreboard(); + } + static bool followCyclic(const V3GraphEdge* edgep) { + const SplitEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type"); + if (oedgep->ignoreThisStep()) return false; + return true; + } +}; +uint32_t SplitEdge::s_stepNum = 0; + +class SplitPostEdge : public SplitEdge { +public: + SplitPostEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) + : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} + virtual ~SplitPostEdge() {} + virtual bool followScoreboard() const { return false; } + virtual string dotColor() const { return "khaki"; } + virtual string dotStyle() const { return ignoreThisStep()?"dotted":V3GraphEdge::dotStyle(); } +}; + +class SplitLVEdge : public SplitEdge { +public: + SplitLVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) + : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} + virtual ~SplitLVEdge() {} + virtual bool followScoreboard() const { return true; } + virtual string dotColor() const { return "yellowGreen"; } + virtual string dotStyle() const { return ignoreThisStep()?"dotted":V3GraphEdge::dotStyle(); } +}; + +class SplitRVEdge : public SplitEdge { +public: + SplitRVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) + : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} + virtual ~SplitRVEdge() {} + virtual bool followScoreboard() const { return true; } + virtual string dotColor() const { return "green"; } + virtual string dotStyle() const { return ignoreThisStep()?"dotted":V3GraphEdge::dotStyle(); } +}; + +struct SplitScorebdEdge : public SplitEdge { +public: + SplitScorebdEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) + : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} + virtual ~SplitScorebdEdge() {} + virtual bool followScoreboard() const { return true; } + virtual string dotColor() const { return "blue"; } + virtual string dotStyle() const { return ignoreThisStep()?"dotted":V3GraphEdge::dotStyle(); } +}; + +struct SplitStrictEdge : public SplitEdge { + // A strict order, based on the original statement order in the graph + // The only non-cutable edge type +public: + SplitStrictEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) + : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL, NOT_CUTABLE) {} + virtual ~SplitStrictEdge() {} + virtual bool followScoreboard() const { return true; } + virtual string dotColor() const { return "blue"; } + virtual string dotStyle() const { return ignoreThisStep()?"dotted":V3GraphEdge::dotStyle(); } +}; + +//###################################################################### +// Split class functions + +class SplitVisitor : public AstNVisitor { +private: + // NODE STATE + // AstVarScope::userp -> Var SplitNodeVertex* for usage var, 0=not set yet + // AstVarScope::user2p -> Var SplitNodeVertex* for delayed assignment var, 0=not set yet + // Ast*::user3p -> Statement SplitLogicVertex* (temporary only) + // Ast*::user4 -> Current ordering number (reorderBlock usage) + + // TYPES + typedef vector VStack; + + // STATE + bool m_reorder; // Reorder statements vs. just splitting + VStack m_stmtStackps; // Current statements being tracked + SplitPliVertex* m_pliVertexp; // Element specifying PLI ordering + V3Graph m_graph; // Scoreboard of var usages/dependencies + bool m_inDly; // Inside ASSIGNDLY + uint32_t m_stepNum; // Step number we need to ignore a edge in + V3Double0 m_statSplits; // Statistic tracking + + //int debug() { return 9; } + + // METHODS + void scoreboardClear() { + //VV***** We reset userp() and user2p on each block!!! + m_inDly = false; + m_graph.clear(); + m_stmtStackps.clear(); + m_pliVertexp = NULL; + AstNode::userClearTree(); + AstNode::user2ClearTree(); + AstNode::user3ClearTree(); + AstNode::user4ClearTree(); + } + + void scoreboardPli() { + // Order all PLI statements with other PLI statements + // This insures $display's and such remain in proper order + // We don't prevent splitting out other non-pli statements, however. + if (!m_pliVertexp) { + m_pliVertexp = new SplitPliVertex(&m_graph); // m_graph.clear() will delete it + } + for (VStack::iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) { + // Both ways... + new SplitScorebdEdge(&m_graph, *it, m_pliVertexp); + new SplitScorebdEdge(&m_graph, m_pliVertexp, *it); + } + } + void scoreboardPushStmt(AstNode* nodep) { + //UINFO(9," push "<user3p()) nodep->v3fatalSrc("user3p should not be used; cleared in processBlock\n"); + nodep->user3p(vertexp); + } + void scoreboardPopStmt() { + //UINFO(9," pop"<nextp()) { + scoreboardPushStmt(nextp); + nextp->accept(*this); + scoreboardPopStmt(); + } + } + + void cleanupBlockGraph(AstNode* nodep) { + // Transform the graph into what we need + UINFO(5, "ReorderBlock "<=9) { + m_graph.dumpDotFilePrefixed("splitg_nodup", false); + //m_graph.dump(); cout<nextp()) { + SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p(); + vvertexp->user(true); + } + + // If a var vertex has only inputs, it's a input-only node, + // and can be ignored for coloring **this block only** + SplitEdge::incrementStep(); + uint32_t numVertexes = 1; // As colors start at 1, not 0 + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { + numVertexes++; + if (!vertexp->outBeginp() && dynamic_cast(vertexp)) { + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { + SplitEdge* oedgep = dynamic_cast(edgep); + oedgep->setIgnoreThisStep(); + } + } + // Mark all logic vertexes not involved with this step as unimportant + if (SplitLogicVertex* vvertexp = dynamic_cast(vertexp)) { + if (!vvertexp->user()) { + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { + SplitEdge* oedgep = dynamic_cast(edgep); + oedgep->setIgnoreThisStep(); + } + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + SplitEdge* oedgep = dynamic_cast(edgep); + oedgep->setIgnoreThisStep(); + } + } + } + } + + // Weak coloring to determine what needs to remain in order + m_graph.weaklyConnected(&SplitEdge::followScoreboard); + + // Add hard orderings between all nodes of same color, in the order they appeared + vector lastOfColor; lastOfColor.resize(numVertexes); + for (uint32_t i=0; inextp()) { + SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p(); + vvertexp->splitColor(vvertexp->color()); + uint32_t color = vvertexp->splitColor(); + if (color >= numVertexes) nextp->v3fatalSrc("More colors then vertexes!\n"); + if (!color) nextp->v3fatalSrc("No node color assigned\n"); + if (lastOfColor[color]) { + new SplitStrictEdge(&m_graph, lastOfColor[color], vvertexp); + } + lastOfColor[color] = vvertexp; + } + + // And a real ordering to get the statements into something reasonable + // We don't care if there's cutable violations here... + //if (debug()>=9) m_graph.dumpDotFilePrefixed((string)"splitg_preo", false); + m_graph.acyclic(&SplitEdge::followCyclic); + m_graph.rank(&SplitEdge::followCyclic); // Or order(), but that's more expensive + if (debug()>=9) m_graph.dumpDotFilePrefixed((string)"splitg_opt", false); + } + + void reorderBlock(AstNode* nodep) { + // Reorder statements in the completed graph + AstAlways* splitAlwaysp = nodep->backp()->castAlways(); + + // Map the rank numbers into nodes they associate with + typedef multimap RankNodeMap; + typedef map ColorRankMap; + ColorRankMap colorRankMap; + uint32_t firstColor = 0; bool multiColors = false; + int currOrder = 0; // Existing sequence number of assignment + for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { + SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p(); + if (!splitAlwaysp) vvertexp->splitColor(1); // All blocks remain as-is + RankNodeMap& rankMap = colorRankMap[vvertexp->splitColor()]; + rankMap.insert(make_pair(vvertexp->rank(), nextp)); + if (firstColor && firstColor != vvertexp->splitColor()) multiColors = true; + firstColor = vvertexp->splitColor(); + nextp->user4(++currOrder); // Record current ordering + } + // If there was only one color, we don't need multiple always blocks + if (!multiColors) splitAlwaysp = NULL; + + // Is the current ordering OK? + bool leaveAlone=true; + if (splitAlwaysp) leaveAlone=false; + int newOrder = 0; // New sequence number of assignment + for (ColorRankMap::iterator colorIt = colorRankMap.begin(); colorIt != colorRankMap.end(); ++colorIt) { + RankNodeMap& rankMap = colorIt->second; + for (RankNodeMap::iterator it = rankMap.begin(); it != rankMap.end(); ++it) { + AstNode* nextp = it->second; + if (++newOrder != nextp->user4()) leaveAlone=false; + } + } + if (leaveAlone) { + UINFO(6," No changes\n"); + } else { + AstNRelinker replaceHandle; // Where to add the list + AstNode* addAfterp = splitAlwaysp; + + for (ColorRankMap::iterator colorIt = colorRankMap.begin(); colorIt != colorRankMap.end(); ++colorIt) { + uint32_t color = colorIt->first; + RankNodeMap& rankMap = colorIt->second; + AstNode* newListp = NULL; + for (RankNodeMap::iterator it = rankMap.begin(); it != rankMap.end(); ++it) { + AstNode* nextp = it->second; + UINFO(6, " Color="<unlinkFrBack(&replaceHandle); + else nextp->unlinkFrBack(); + newListp = newListp->addNext(nextp); + } + if (splitAlwaysp) { + m_statSplits++; + AstAlways* alwaysp = new AstAlways(newListp->fileline(), NULL, NULL); + addAfterp->addNextHere(alwaysp); addAfterp=alwaysp; + alwaysp->addStmtp(newListp); + } else { + // Just reordering + replaceHandle.relink(newListp); + } + } + if (splitAlwaysp) { + pushDeletep(splitAlwaysp->unlinkFrBack()); + } + } // leaveAlone + } + + void processBlock(AstNode* nodep) { + if (!nodep) return; // Empty lists are ignorable + // Pass the first node in a list of block items, we'll process them + // Check there's >= 2 sub statements, else nothing to analyze + // Save recursion state + void* oldBlockUser3 = nodep->user3p(); // May be overloaded in below loop, save it + nodep->user3p(NULL); + // Process it + if (!nodep->nextp()) { + // Just one, so can't reorder. Just look for more blocks/statements. + nodep->accept(*this); + } else { + UINFO(9," processBlock "<nextp()) { + SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p(); + vvertexp->unlinkDelete(&m_graph); + } + } + nodep->user3p(oldBlockUser3); + } + + // VISITORS + virtual void visit(AstAlways* nodep, AstNUser*) { + UINFO(4," ALW "<=9) nodep->dumpTree(cout," alwIn:: "); + scoreboardClear(); + processBlock(nodep->bodysp()); + if (debug()>=9) nodep->dumpTree(cout," alwOut: "); + } + virtual void visit(AstNodeIf* nodep, AstNUser*) { + if (!m_reorder) { + nodep->iterateChildren(*this); + } else { + UINFO(4," IF "<condp()->iterateAndNext(*this); + processBlock(nodep->ifsp()); + processBlock(nodep->elsesp()); + } + } + // We don't do AstNodeFor/AstWhile loops, due to the standard question + // of what is before vs. after + + virtual void visit(AstAssignDly* nodep, AstNUser*) { + m_inDly = true; + UINFO(4," ASSIGNDLY "<iterateChildren(*this); + m_inDly = false; + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + if (!m_stmtStackps.empty()) { + AstVarScope* vscp = nodep->varScopep(); + if (!vscp) nodep->v3fatalSrc("Not linked"); + if (!nodep->varp()->isConst()) { // Constant lookups can be ignored + if (nodep->varp()->isSigPublic()) { + // Public signals shouldn't be changed, pli code might be messing with them + scoreboardPli(); + } + + // Create vertexes for variable + if (!vscp->userp()) { + SplitVarStdVertex* vstdp = new SplitVarStdVertex(&m_graph, vscp); + vscp->userp(vstdp); + } + SplitVarStdVertex* vstdp = (SplitVarStdVertex*) vscp->userp(); + + // SPEEDUP: We add duplicate edges, that should be fixed + if (m_inDly && nodep->lvalue()) { + UINFO(4," VARREFDLY: "<user2p()) { + SplitVarPostVertex* vpostp = new SplitVarPostVertex(&m_graph, vscp); + vscp->user2p(vpostp); + new SplitPostEdge(&m_graph, vstdp, vpostp); + } + SplitVarPostVertex* vpostp = (SplitVarPostVertex*)vscp->user2p(); + // Add edges + for (VStack::iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) { + new SplitLVEdge(&m_graph, vpostp, *it); + } + } else { // Nondelayed assignment + if (nodep->lvalue()) { + // Non-delay; need to maintain existing ordering with all consumers of the signal + UINFO(4," VARREFLV: "<isSplittable()) { + UINFO(9," NotSplittable "<iterateChildren(*this); + } + +public: + // CONSTUCTORS + SplitVisitor(AstNode* nodep, bool reorder) + : m_reorder(reorder) { + scoreboardClear(); + nodep->accept(*this); + } + virtual ~SplitVisitor() { + V3Stats::addStat("Optimizations, Split always", m_statSplits); + } +}; + +//###################################################################### +// Split class functions + +void V3Split::splitReorderAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Stats.h" +#include "V3Ast.h" +#include "V3File.h" + +//###################################################################### +// Stats class functions + +class StatsVisitor : public AstNVisitor { +private: + // NODE STATE/TYPES + // STATE + string m_stage; // Name of the stage we are scanning + bool m_fast; // Counting only fastpath + + AstCFunc* m_cfuncp; // Current CFUNC + V3Double0 m_statInstrLong; // Instruction count + bool m_counting; // Currently counting + double m_instrs; // Current instr count + + vector m_statTypeCount; // Nodes of given type + V3Double0 m_statAbove[AstType::_ENUM_END][AstType::_ENUM_END]; // Nodes of given type + V3Double0 m_statPred[AstBranchPred::_ENUM_END]; // Nodes of given type + V3Double0 m_statInstr; // Instruction count + V3Double0 m_statInstrFast; // Instruction count + vector m_statVarWidths; // Variables of given type + V3Double0 m_statVarArray; // Statistic tracking + V3Double0 m_statVarBytes; // Statistic tracking + V3Double0 m_statVarClock; // Statistic tracking + V3Double0 m_statVarScpBytes; // Statistic tracking + + // METHODS + void allNodes(AstNode* nodep) { + m_instrs += nodep->instrCount(); + if (m_counting) { + ++m_statTypeCount[nodep->type()]; + if (nodep->backp() && nodep->backp()->nextp()!=nodep) { + // Grab only those above, not those "back" + ++m_statAbove[nodep->backp()->type()][nodep->type()]; + } + m_statInstr += nodep->instrCount(); + if (m_cfuncp && !m_cfuncp->slow()) m_statInstrFast += nodep->instrCount(); + } + } + + // VISITORS + virtual void visit(AstModule* nodep, AstNUser*) { + allNodes(nodep); + if (!m_fast) { + nodep->iterateChildren(*this); + } else { + for (AstNode* searchp = nodep->stmtsp(); searchp; searchp=searchp->nextp()) { + if (AstCFunc* funcp = searchp->castCFunc()) { + if (funcp->name() == "_eval") { + m_instrs=0; + m_counting = true; + funcp->iterateChildren(*this); + m_counting = false; + } + } + } + } + } + virtual void visit(AstVar* nodep, AstNUser*) { + allNodes(nodep); + nodep->iterateChildren(*this); + if (m_counting) { + if (nodep->isUsedClock()) ++m_statVarClock; + if (nodep->arraysp()) ++m_statVarArray; + if (!nodep->arraysp()) m_statVarBytes += nodep->widthTotalBytes(); + if (int(m_statVarWidths.size()) <= nodep->width()) { + m_statVarWidths.resize(nodep->width()+5); + } + ++ m_statVarWidths.at(nodep->width()); + } + } + virtual void visit(AstVarScope* nodep, AstNUser*) { + allNodes(nodep); + nodep->iterateChildren(*this); + if (m_counting) { + if (!nodep->varp()->arraysp()) m_statVarScpBytes += nodep->varp()->widthTotalBytes(); + } + } + virtual void visit(AstNodeIf* nodep, AstNUser*) { + UINFO(4," IF "<condp()->iterateAndNext(*this); + // Track prediction + if (m_counting) { + ++m_statPred[nodep->branchPred()]; + } + if (!m_fast) { + nodep->iterateChildren(*this); + } else { + // See which path we want to take + bool takeElse = false; + if (!nodep->elsesp() || (nodep->branchPred()==AstBranchPred::LIKELY)) { + // Always take the if + } else if (!nodep->ifsp() || (nodep->branchPred()==AstBranchPred::UNLIKELY)) { + // Always take the else + } else { + // Take the longer path + bool prevCounting = m_counting; + double prevInstr = m_instrs; + m_counting = false; + // Check if + m_instrs = 0; + nodep->ifsp()->iterateAndNext(*this); + double instrIf = m_instrs; + // Check else + m_instrs = 0; + nodep->elsesp()->iterateAndNext(*this); + double instrElse = m_instrs; + // Max of if or else condition + takeElse = (instrElse > instrIf); + // Restore + m_counting = prevCounting; + m_instrs = prevInstr + (takeElse?instrElse:instrIf); + } + // Count the block + if (m_counting) { + if (takeElse) { + nodep->elsesp()->iterateAndNext(*this); + } else { + nodep->ifsp()->iterateAndNext(*this); + } + } + } + } + // While's we assume evaluate once. + //virtual void visit(AstWhile* nodep, AstNUser*) { + + virtual void visit(AstCCall* nodep, AstNUser*) { + //UINFO(4," CCALL "<iterateChildren(*this); + if (m_fast) { + // Enter the function and trace it + nodep->funcp()->accept(*this); + } + } + virtual void visit(AstCFunc* nodep, AstNUser*) { + m_cfuncp = nodep; + allNodes(nodep); + nodep->iterateChildren(*this); + m_cfuncp = NULL; + } + virtual void visit(AstNode* nodep, AstNUser*) { + allNodes(nodep); + nodep->iterateChildren(*this); + } +public: + // CONSTRUCTORS + StatsVisitor(AstNetlist* nodep, const string& stage, bool fast) + : m_stage(stage), m_fast(fast) + { + m_cfuncp = NULL; + m_counting = !m_fast; + m_instrs = 0; + // Initialize arrays + m_statTypeCount.resize(AstType::_ENUM_END); + // Process + nodep->accept(*this); + } + virtual ~StatsVisitor() { + // Done. Publish statistics + V3Stats::addStat(m_stage, "Instruction count, TOTAL", m_statInstr); + V3Stats::addStat(m_stage, "Instruction count, fast", m_statInstrFast); + // Vars + V3Stats::addStat(m_stage, "Vars, arrayed", m_statVarArray); + V3Stats::addStat(m_stage, "Vars, clock attribute", m_statVarClock); + V3Stats::addStat(m_stage, "Var space, non-arrays, bytes", m_statVarBytes); + if (m_statVarScpBytes) { + V3Stats::addStat(m_stage, "Var space, scoped, bytes", m_statVarScpBytes); + } + for (unsigned i=0; icount(); + otherp->m_printit = false; + } + // CONSTRUCTORS + V3Statistic(const string& stage, const string& name, double count, bool sumit=false) + : m_name(name), m_count(count), m_stage(stage), m_sumit(sumit) + , m_printit(true) {} + virtual ~V3Statistic() {} +}; + +//============================================================================ + +class V3Stats { +public: + static void addStat(const V3Statistic&); + static void addStat(const string& stage, const string& name, double count) { + addStat(V3Statistic(stage,name,count)); } + static void addStat(const string& name, double count) { + addStat(V3Statistic("*",name,count)); } + static void addStatSum(const string& name, double count) { + addStat(V3Statistic("*",name,count,true)); } + /// Called by the top level to collect statistics + static void statsStageAll(AstNetlist* nodep, const string& stage, bool fast=false); + static void statsFinalAll(AstNetlist* nodep); + /// Called by the top level to dump the statistics + static void statsReport(); +}; + + +#endif // Guard diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp new file mode 100644 index 000000000..44cb95e06 --- /dev/null +++ b/src/V3StatsReport.cpp @@ -0,0 +1,218 @@ +// $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 +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Stats.h" +#include "V3Ast.h" +#include "V3File.h" + +//###################################################################### +// Stats dumping + +class StatsReport { + // TYPES + typedef vector StatColl; + + // STATE + ofstream& os; // Output stream + static StatColl s_allStats; ///< All statistics + + void header() { + os<<"Verilator Statistics Report\n"; + os< ByName; + ByName byName; + // * is always first + for (StatColl::iterator it = s_allStats.begin(); it!=s_allStats.end(); ++it) { + V3Statistic* repp = &(*it); + byName.insert(make_pair(repp->name(), repp)); + } + + // Process duplicates + V3Statistic* lastp = NULL; + for (ByName::iterator it = byName.begin(); it!=byName.end(); ++it) { + V3Statistic* repp = it->second; + if (lastp && lastp->sumit() && lastp->printit() + && lastp->name() == repp->name() && lastp->stage() == repp->stage()) { + repp->combineWith(lastp); + } + lastp = repp; + } + } + + void stars() { + os<<"Global Statistics:\n"; + os< ByName; + ByName byName; + // * is always first + for (StatColl::iterator it = s_allStats.begin(); it!=s_allStats.end(); ++it) { + const V3Statistic* repp = &(*it); + if (repp->stage() == "*" && repp->printit()) { + if (maxWidth < repp->name().length()) maxWidth = repp->name().length(); + byName.insert(make_pair(repp->name(), repp)); + } + } + + // Print organized by stage + for (ByName::iterator it = byName.begin(); it!=byName.end(); ++it) { + const V3Statistic* repp = it->second; + os<<" "<name(); + repp->dump(os); + os< Stages; + Stages stages; + map stageInt; + typedef multimap ByName; + ByName byName; + // * is always first + for (StatColl::iterator it = s_allStats.begin(); it!=s_allStats.end(); ++it) { + const V3Statistic* repp = &(*it); + if (repp->stage() != "*" && repp->printit()) { + if (maxWidth < repp->name().length()) maxWidth = repp->name().length(); + if (stageInt.find(repp->stage()) == stageInt.end()) { + stageInt.insert(make_pair(repp->stage(), stage++)); + stages.push_back(repp->stage()); + } + byName.insert(make_pair(repp->name(), repp)); + } + } + + // Header + os<<" Stat "<second; + if (lastName != repp->name()) { + lastName = repp->name(); + { + string commaName = lastName; + string::size_type pos; + if ((pos=commaName.find(",")) != string::npos) { + commaName.erase(pos); + } + if (lastCommaName != commaName) { + lastCommaName = commaName; + os<name(); + } + while (colstage()) { + os<dump(os); + col++; + } + os<fail()) v3fatalSrc("Can't write "<close(); delete ofp; ofp = NULL; +} diff --git a/src/V3Subst.cpp b/src/V3Subst.cpp new file mode 100644 index 000000000..3c301dea2 --- /dev/null +++ b/src/V3Subst.cpp @@ -0,0 +1,294 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Substitute constants and expressions in expr temp's +// +// 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. +// +//************************************************************************* +// V3Subst's Transformations: +// +// Each module: +// Search all ASSIGN(WORDSEL(...)) and build what it's assigned to +// Later usages of that word may then be replaced +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Subst.h" +#include "V3Const.h" +#include "V3Stats.h" +#include "V3Ast.h" + +//###################################################################### +// Common debugging baseclass + +class SubstBaseVisitor : public AstNVisitor { +public: + static int debug() { return 0; } +// static int debug() { return 9; } +}; + +//###################################################################### +// Subst state, as a visitor of each AstNode + +class SubstVarEntry { + AstVar* m_varp; // Variable this tracks + bool m_complexAssign;// True if assigned multiple times or in complex mannor + AstNodeAssign* m_wholeAssignp; // Last assignment to entire array + bool m_wholeUse; // True if used as entire wide array + bool m_wordAssign; // True if any word assignments + bool m_wordUse; // True if any individual word usage + vector m_wordAssignps; // Last assignment to each word of this var + vector m_wordUses; // True if each word was consumed + vector m_wordComplex; // True if each word is complex + int debug() { return SubstBaseVisitor::debug(); } +public: + SubstVarEntry (AstVar* varp) { // Construction for when a var is used + m_varp = varp; + m_complexAssign = false; + m_wholeAssignp = NULL; + m_wholeUse = false; + m_wordAssign = false; + m_wordUse = false; + m_wordAssignps.resize(varp->widthWords()); + m_wordUses.resize(varp->widthWords()); + m_wordComplex.resize(varp->widthWords()); + for (int i=0; iwidthWords(); i++) { + m_wordAssignps[i] = NULL; + m_wordUses[i] = false; + m_wordComplex[i] = false; + } + } + bool wordNumOk(int word) { + return word < m_varp->widthWords(); + } + AstNodeAssign* getWordAssignp(int word) { + if (!wordNumOk(word)) return NULL; + else return m_wordAssignps[word]; + } + void assignWhole (AstNodeAssign* assp) { + if (m_wholeAssignp) m_complexAssign = true; + m_wholeAssignp = assp; + } + void assignWord (int word, AstNodeAssign* assp) { + if (!wordNumOk(word) || getWordAssignp(word) || m_wordComplex[word]) m_complexAssign = true; + m_wordAssign = true; + if (wordNumOk(word)) m_wordAssignps[word] = assp; + } + void assignWordComplex (int word) { + if (!wordNumOk(word) || getWordAssignp(word) || m_wordComplex[word]) m_complexAssign = true; + m_wordComplex[word] = true; + } + void assignComplex() { + m_complexAssign = true; + } + void consumeWhole() { //==consumeComplex as we don't know the difference + m_wholeUse = true; + } + void consumeWord(int word) { + m_wordUses[word] = true; + m_wordUse = true; + } + // ACCESSORS + AstNode* substWhole(AstNode* errp) { + if (!m_varp->isWide() + && !m_complexAssign && m_wholeAssignp && !m_wordAssign) { + AstNodeAssign* assp = m_wholeAssignp; + if (!assp) errp->v3fatalSrc("Reading whole that was never assigned"); + return (assp->rhsp()); + } else { + return NULL; + } + } + AstNode* substWord(AstNode* errp, int word) { // Return what to substitute given word number for + if (!m_complexAssign && !m_wholeAssignp && !m_wordComplex[word]) { + AstNodeAssign* assp = getWordAssignp(word); + if (!assp) errp->v3fatalSrc("Reading a word that was never assigned, or bad word #"); + return (assp->rhsp()); + } else { + return NULL; + } + } + void deleteAssign (AstNodeAssign* nodep) { + UINFO(5, "Delete "<unlinkFrBack()->deleteTree(); nodep=NULL; + } + void deleteUnusedAssign() { + // If there are unused assignments in this var, kill them + if (!m_wholeUse && !m_wordUse && m_wholeAssignp) { + deleteAssign (m_wholeAssignp); m_wholeAssignp=NULL; + } + for (unsigned i=0; i SubstVar* for usage var, 0=not set yet + // + // STATE + vector m_entryps; // Nodes to delete when we are finished + int m_ops; // Number of operators on assign rhs + V3Double0 m_statSubsts; // Statistic tracking + + enum { SUBST_MAX_OPS_SUBST = 30, // Maximum number of ops to substitute in + SUBST_MAX_OPS_NA = 9999 }; // Not allowed to substitute + + // METHODS + SubstVarEntry* getEntryp(AstVarRef* nodep) { + if (!nodep->varp()->userp()) { + SubstVarEntry* entryp = new SubstVarEntry (nodep->varp()); + m_entryps.push_back(entryp); + nodep->varp()->userp(entryp); + return entryp; + } else { + SubstVarEntry* entryp = (SubstVarEntry*)(nodep->varp()->userp()); + return entryp; + } + } + + // VISITORS + virtual void visit(AstNodeAssign* nodep, AstNUser*) { + m_ops = 0; + nodep->rhsp()->iterateAndNext(*this); + bool hit=false; + if (AstVarRef* varrefp = nodep->lhsp()->castVarRef()) { + if (varrefp->varp()->isStatementTemp()) { + SubstVarEntry* entryp = getEntryp(varrefp); + hit = true; + if (m_ops > SUBST_MAX_OPS_SUBST) { + UINFO(8," ASSIGNtooDeep "<assignComplex(); + } else { + UINFO(8," ASSIGNwhole "<assignWhole(nodep); + } + } + } + else if (AstWordSel* wordp = nodep->lhsp()->castWordSel()) { + if (AstVarRef* varrefp = wordp->lhsp()->castVarRef()) { + if (wordp->rhsp()->castConst() + && varrefp->varp()->isStatementTemp()) { + int word = wordp->rhsp()->castConst()->asInt(); + SubstVarEntry* entryp = getEntryp(varrefp); + hit = true; + if (m_ops > SUBST_MAX_OPS_SUBST) { + UINFO(8," ASSIGNtooDeep "<assignWordComplex(word); + } else { + UINFO(8," ASSIGNword"<assignWord(word, nodep); + } + } + } + } + if (!hit) { + nodep->lhsp()->accept(*this); + } + } + void replaceSubstEtc(AstNode* nodep, AstNode* substp) { + if (debug()>5) nodep->dumpTree(cout," substw_old: "); + AstNode* newp = substp->cloneTree(true); + if (!nodep->isQuad() && newp->isQuad()) { + newp = new AstCast (newp->fileline(), newp, nodep); + } + if (debug()>5) newp->dumpTree(cout," w_new: "); + nodep->replaceWith(newp); + pushDeletep(nodep); nodep=NULL; + m_statSubsts++; + } + virtual void visit(AstWordSel* nodep, AstNUser*) { + nodep->rhsp()->accept(*this); + AstVarRef* varrefp = nodep->lhsp()->castVarRef(); + AstConst* constp = nodep->rhsp()->castConst(); + if (varrefp && varrefp->varp()->isStatementTemp() + && !varrefp->lvalue() + && constp) { + // Nicely formed lvalues handled in NodeAssign + // Other lvalues handled as unknown mess in AstVarRef + int word = constp->asInt(); + UINFO(8," USEword"<substWord (nodep, word)) { + replaceSubstEtc(nodep, substp); nodep=NULL; + } else { + entryp->consumeWord(word); + } + } else { + nodep->lhsp()->accept(*this); + } + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + if (nodep->varp()->isStatementTemp()) { + SubstVarEntry* entryp = getEntryp (nodep); + if (nodep->lvalue()) { + UINFO(8," ASSIGNcpx "<assignComplex(); + } else if (AstNode* substp = entryp->substWhole(nodep)) { + UINFO(8," USEwhole "<consumeWhole(); + } + } + } + virtual void visit(AstVar* nodep, AstNUser*) {} + virtual void visit(AstNode* nodep, AstNUser*) { + m_ops++; + if (!nodep->isSubstOptimizable()) { + m_ops = SUBST_MAX_OPS_NA; + } + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + SubstVisitor(AstNode* nodep) { + AstNode::userClearTree(); // userp() used on entire tree + m_ops = 0; + nodep->accept(*this); + } + virtual ~SubstVisitor() { + V3Stats::addStat("Optimizations, Substituted temps", m_statSubsts); + for (vector::iterator it = m_entryps.begin(); it != m_entryps.end(); ++it) { + (*it)->deleteUnusedAssign(); + delete (*it); + } + } +}; + +//###################################################################### +// Subst class functions + +void V3Subst::substituteAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include + +#include "V3Global.h" + +//###################################################################### +// Symbol table + +class V3SymTable : public AstNUser { + // Symbol table that can have a "superior" table for resolving upper references + private: + // MEMBERS + typedef std::map IdNameMap; + IdNameMap m_idNameMap; // Hash of variables by name + V3SymTable* m_upperp; // Table "above" this one in name scope + public: + // METHODS + V3SymTable(V3SymTable* upperTablep) { m_upperp = upperTablep; } + V3SymTable() { m_upperp = NULL; } + ~V3SymTable() {} + void insert(const string& name, AstNode* nodep) { + //UINFO(9, " SymInsert "<v3fatalSrc("Inserting two symbols with same name: "<second); + // Then scan the upper begin/end block or module for the name + if (m_upperp) return m_upperp->findIdName(name); + return NULL; + } +}; + +#endif // guard diff --git a/src/V3Table.cpp b/src/V3Table.cpp new file mode 100644 index 000000000..616074e2e --- /dev/null +++ b/src/V3Table.cpp @@ -0,0 +1,744 @@ +// $Id$ +//************************************************************************* +// DESCRIPTION: Verilator: Make lookup tables +// +// 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. +// +//************************************************************************* +// TABLE TRANSFORMATIONS: +// Look at all large always and assignments. +// Count # of input bits and # of output bits, and # of statements +// If high # of statements relative to inpbits*outbits, +// replace with lookup table +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Table.h" +#include "V3Stats.h" +#include "V3Ast.h" + +//###################################################################### +// Table class functions + +// CONFIG +static const double TABLE_MAX_BYTES = 1*1024*1024; // 1MB is max table size (better be lots of instructs to be worth it!) +static const double TABLE_TOTAL_BYTES = 64*1024*1024; // 64MB is close to max memory of some systems (256MB or so), so don't get out of control +static const double TABLE_SPACE_TIME_MULT = 8; // Worth 8 bytes of data to replace a instruction +static const int TABLE_MIN_NODE_COUNT = 32; // If < 32 instructions, not worth the effort + +class TableVisitor; + +//###################################################################### + +class TableBaseVisitor : public AstNVisitor { +public: + // Note level 8&9 include debugging each simulation value +// int debug() { return 7; } +// int debug() { return 9; } +}; + +//###################################################################### + +class TableSimulateVisitor : public TableBaseVisitor { + // Simulate a node tree, returning value of variables + // Two major operating modes: + // Test the tree to see if it is conformant + // Given a set of input values, find the output values + // Both are done in this same visitor to reduce risk; if a visitor + // is missing, we will simply not apply the optimization, rather then bomb. + +private: + // NODE STATE + // Cleared on each always/assignw + // Checking: + // AstVarScope::user() -> VarUsage. Set true to indicate tracking as lvalue/rvalue + // Simulating: + // AstVarScope::user3() -> V3Number*. Value of variable or node + // AstVarScope::user4() -> V3Number*. Last value output was set to + + enum VarUsage { VU_NONE=0, VU_LV=1, VU_RV=2, VU_LVDLY=4 }; + + // STATE + bool m_checking; ///< Checking vs. simulation mode + // Checking: + TableVisitor* m_cbthis; ///< Class for callback + const char* m_whyNotOptimizable; ///< String explaining why not optimizable or NULL to optimize + bool m_anyAssignDly; ///< True if found a delayed assignment + bool m_anyAssignComb; ///< True if found a non-delayed assignment + bool m_inDlyAssign; ///< Under delayed assignment + int m_instrCount; ///< Number of nodes + int m_dataCount; ///< Bytes of data + // Simulating: + deque m_numFreeps; ///< List of all numbers free and not in use + deque m_numAllps; ///< List of all numbers free and in use + + // Checking METHODS +public: + void varRefCb(AstVarRef* nodep); ///< Call other-this function on all new var references + + void clearOptimizable(AstNode* nodep/*null ok*/, const char* why) { + if (!m_whyNotOptimizable) { + if (debug()>=5) { + UINFO(0,"Clear optimizable: "<user3p()) { + // Save time - kept a list of allocated but unused V3Numbers + // It would be more efficient to do this by size, but the extra accounting + // slows things down more then we gain. + V3Number* nump; + if (!m_numFreeps.empty()) { + //UINFO(7,"Num Reuse "<width()<width(nodep->width()); + nump->fileline(nodep->fileline()); + nump->setLong(value); // We do support more then 32 bit numbers, just valuep=0 in that case + } else { + //UINFO(7,"Num New "<width()<fileline(), nodep->width(), value); + m_numAllps.push_back(nump); + } + setNumber(nodep, nump); + } + return (fetchNumber(nodep)); + } + V3Number* fetchNumberNull(AstNode* nodep) { + return ((V3Number*)nodep->user3p()); + } + V3Number* fetchNumber(AstNode* nodep) { + V3Number* nump = fetchNumberNull(nodep); + if (!nump) nodep->v3fatalSrc("No value found for node."); + return nump; + } +private: + void setNumber(AstNode* nodep, const V3Number* nump) { + UINFO(9," set num "<<*nump<<" on "<user3p((AstNUser*)nump); + } + + void checkNodeInfo(AstNode* nodep) { + m_instrCount += nodep->instrCount(); + m_dataCount += nodep->width(); + if (!nodep->isPredictOptimizable()) { + //UINFO(9," !predictopt "<iterateChildren(*this); + } + virtual void visit(AstSenTree* nodep, AstNUser*) { + // Sensitivities aren't inputs per se; we'll keep our tree under the same sens. + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + AstVarScope* vscp = nodep->varScopep(); + if (!vscp) nodep->v3fatalSrc("Not linked"); + if (m_checking) { + if (m_checking && !optimizable()) return; // Accelerate + // We can't have non-delayed assignments with same value on LHS and RHS + // as we don't figure out variable ordering. + // Delayed is OK though, as we'll decode the next state separately. + if (nodep->varp()->arraysp()) clearOptimizable(nodep,"Array references"); + if (nodep->lvalue()) { + if (m_inDlyAssign) { + if (!(vscp->user() & VU_LVDLY)) { + vscp->user( vscp->user() | VU_LVDLY); + varRefCb (nodep); + } + } else { // nondly asn + if (!(vscp->user() & VU_LV)) { + if (vscp->user() & VU_RV) clearOptimizable(nodep,"Var read & write"); + vscp->user( vscp->user() | VU_LV); + varRefCb (nodep); + } + } + } else { + if (!(vscp->user() & VU_RV)) { + if (vscp->user() & VU_LV) clearOptimizable(nodep,"Var write & read"); + vscp->user( vscp->user() | VU_RV); + varRefCb (nodep); + } + } + } + else { // simulating + if (nodep->lvalue()) { + nodep->v3fatalSrc("LHS varref should be handled in AstAssign visitor."); + } else { + // Return simulation value + V3Number* nump = fetchNumberNull(vscp); + if (!nump) nodep->v3fatalSrc("Variable value should have been set before any visitor called."); + setNumber(nodep, nump); + } + } + } + virtual void visit(AstNodeIf* nodep, AstNUser*) { + if (m_checking) { + checkNodeInfo(nodep); + nodep->iterateChildren(*this); + } else { + nodep->condp()->iterateAndNext(*this); + if (fetchNumber(nodep->condp())->isNeqZero()) { + nodep->ifsp()->iterateAndNext(*this); + } else { + nodep->elsesp()->iterateAndNext(*this); + } + } + } + virtual void visit(AstConst* nodep, AstNUser*) { + if (m_checking) { + checkNodeInfo(nodep); + } else { + setNumber(nodep, &(nodep->num())); + } + } + virtual void visit(AstNodeUniop* nodep, AstNUser*) { + if (m_checking) { + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + nodep->iterateChildren(*this); + } else { + nodep->iterateChildren(*this); + nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp())); + } + } + virtual void visit(AstNodeBiop* nodep, AstNUser*) { + if (m_checking) { + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + nodep->iterateChildren(*this); + } else { + nodep->iterateChildren(*this); + nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp()), *fetchNumber(nodep->rhsp())); + } + } + virtual void visit(AstNodeTriop* nodep, AstNUser*) { + if (m_checking) { + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + nodep->iterateChildren(*this); + } else { + nodep->iterateChildren(*this); + nodep->numberOperate(*newNumber(nodep), + *fetchNumber(nodep->lhsp()), + *fetchNumber(nodep->rhsp()), + *fetchNumber(nodep->thsp())); + } + } + virtual void visit(AstNodeCond* nodep, AstNUser*) { + // We could use above visit(AstNodeTriop), but it's slower to evaluate + // both sides when we really only need to evaluate one side. + if (m_checking) { + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + nodep->iterateChildren(*this); + } else { + nodep->condp()->accept(*this); + if (fetchNumber(nodep->condp())->isNeqZero()) { + nodep->expr1p()->accept(*this); + newNumber(nodep)->opAssign(*fetchNumber(nodep->expr1p())); + } else { + nodep->expr2p()->accept(*this); + newNumber(nodep)->opAssign(*fetchNumber(nodep->expr2p())); + } + } + } + virtual void visit(AstNodeAssign* nodep, AstNUser*) { + if (m_checking) { + if (!optimizable()) return; // Accelerate + if (nodep->castAssignDly()) { + if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non dly assigns"); + m_anyAssignDly = true; + m_inDlyAssign = true; + } else { + if (m_anyAssignDly) clearOptimizable(nodep, "Mix of dly/non dly assigns"); + m_anyAssignComb = true; + } + nodep->iterateChildren(*this); + } + if (!nodep->lhsp()->castVarRef()) { + clearOptimizable(nodep, "LHS isn't simple variable"); + } + else if (!m_checking) { + nodep->rhsp()->iterateAndNext(*this); + AstVarScope* vscp = nodep->lhsp()->castVarRef()->varScopep(); + setNumber(vscp, fetchNumber(nodep->rhsp())); + } + m_inDlyAssign = false; + } + + virtual void visit(AstComment*, AstNUser*) {} + // default + // These types are definately not reducable + // AstCoverInc, AstNodePli, AstArraySel, AstStop, AstFinish, + // AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt + // In theory, we could follow the loop, but might be slow + // AstFor, AstWhile + virtual void visit(AstNode* nodep, AstNUser*) { + if (m_checking) { + checkNodeInfo(nodep); + if (optimizable()) { + // Hmm, what is this then? + // In production code, we'll just not optimize. It should be fixed though. + clearOptimizable(nodep, "Unknown node type, perhaps missing visitor in TableSimulateVisitor"); +#ifdef VL_DEBUG + UINFO(0,"Unknown node type in TableSimulateVisitor: "<typeName()<v3fatalSrc("Optimizable should have been cleared in check step, and never reach simulation."); + } + } + +public: + // CONSTRUCTORS + TableSimulateVisitor(TableVisitor* cbthis, bool checking) { + m_cbthis = cbthis; + m_checking = checking; + clear(); // We reuse this structure in the main loop, so put initializers inside clear() + } + void clear() { + m_whyNotOptimizable = NULL; + m_anyAssignComb = false; + m_anyAssignDly = false; + m_inDlyAssign = false; + m_instrCount = 0; + m_dataCount = 0; + + AstNode::userClearTree(); // userp() used on entire tree + AstNode::user3ClearTree(); // user3p() used on entire tree + AstNode::user4ClearTree(); // user4p() used on entire tree + + // Move all allocated numbers to the free pool + m_numFreeps = m_numAllps; + } + void main (AstNode* nodep) { + nodep->accept(*this); + } + virtual ~TableSimulateVisitor() { + for (deque::iterator it = m_numAllps.begin(); it != m_numAllps.end(); ++it) { + delete (*it); + } + m_numFreeps.clear(); + m_numAllps.clear(); + } +}; + + +//###################################################################### +// Table class functions + +class TableVisitor : public TableBaseVisitor { +private: + // NODE STATE + // Cleared on each always/assignw + + // STATE + double m_totalBytes; // Total bytes in tables created + V3Double0 m_statTablesCre; // Statistic tracking + + // State cleared on each module + AstModule* m_modp; // Current MODULE + int m_modTables; // Number of tables created in this module + deque m_modTableVscs; // All tables created + + // State cleared on each scope + AstScope* m_scopep; // Current SCOPE + + // State cleared on each always/assignw + bool m_assignDly; // Consists of delayed assignments instead of normal assignments + int m_inWidth; // Input table width + int m_outWidth; // Output table width + deque m_inVarps; // Input variable list + deque m_outVarps; // Output variable list + deque m_outNotSet; // True if output variable is not set at some point + + // When creating a table + deque m_tableVarps; // Table being created + + // METHODS + bool treeTest(AstAlways* nodep) { + // Process alw/assign tree + m_inWidth = 0; + m_outWidth = 0; + m_inVarps.clear(); + m_outVarps.clear(); + m_outNotSet.clear(); + + // Collect stats + TableSimulateVisitor chkvis (this, true); + chkvis.main(nodep); + m_assignDly = chkvis.isAssignDly(); + // Also sets m_inWidth + // Also sets m_outWidth + // Also sets m_inVarps + // Also sets m_outVarps + + // Calc data storage in bytes + int chgWidth = m_outVarps.size(); // Width of one change-it-vector + if (chgWidth<8) chgWidth = 8; + double space = (pow((double)2,((double)(m_inWidth))) + *(double)(m_outWidth+chgWidth)); + // Instruction count bytes (ok, it's space also not time :) + double bytesPerInst = 4; + double time = (chkvis.instrCount()*bytesPerInst + chkvis.dataCount()) + 1; // +1 so won't div by zero + if (chkvis.instrCount() < TABLE_MIN_NODE_COUNT) { + chkvis.clearOptimizable(nodep,"Too few nodes involved"); + } + if (space > TABLE_MAX_BYTES) { + chkvis.clearOptimizable(nodep,"Too much space"); + } + if (space > time * TABLE_SPACE_TIME_MULT) { + chkvis.clearOptimizable(nodep,"Bad tradeoff"); + } + if (m_totalBytes > TABLE_TOTAL_BYTES) { + chkvis.clearOptimizable(nodep,"Out of memory"); + } + if (!m_outWidth || !m_inWidth) { + chkvis.clearOptimizable(nodep,"No I/O"); + } + UINFO(4, " Test: Opt="<<(chkvis.optimizable()?"OK":"NO") + <<", Instrs="<varScopep(); + if (nodep->lvalue()) { + m_outWidth += nodep->varp()->widthTotalBytes(); + m_outVarps.push_back(vscp); + } else { + // We'll make the table with a separate natural alignment for each + // output var, so always have char, 16 or 32 bit widths, so use widthTotalBytes + m_inWidth += nodep->varp()->width(); // Space for var + m_inVarps.push_back(vscp); + } + } + +private: + void createTable(AstAlways* nodep) { + // We've determined this table of nodes is optimizable, do it. + m_modTables++; + m_statTablesCre++; + + // Index into our table + AstVar* indexVarp = new AstVar (nodep->fileline(), AstVarType::BLOCKTEMP, + "__Vtableidx_" + cvtToStr(m_modTables), + new AstRange (nodep->fileline(), m_inWidth-1, 0)); + m_modp->addStmtp(indexVarp); + AstVarScope* indexVscp = new AstVarScope (indexVarp->fileline(), m_scopep, indexVarp); + m_scopep->addVarp(indexVscp); + + // Change it variable + AstVar* chgVarp = new AstVar (nodep->fileline(), AstVarType::MODULETEMP, + "__Vtablechg_" + cvtToStr(m_modTables), + new AstRange (nodep->fileline(), m_outVarps.size()-1, 0), + new AstRange (nodep->fileline(), VL_MASK_I(m_inWidth), 0)); + chgVarp->isConst(true); + chgVarp->initp(new AstInitArray (nodep->fileline(), NULL)); + m_modp->addStmtp(chgVarp); + AstVarScope* chgVscp = new AstVarScope (chgVarp->fileline(), m_scopep, chgVarp); + m_scopep->addVarp(chgVscp); + + createTableVars(nodep); + AstNode* stmtsp = createLookupInput(nodep, indexVscp); + createTableValues(nodep, chgVscp); + + // Collapse duplicate tables + chgVscp = findDuplicateTable(chgVscp); + for (deque::iterator it = m_tableVarps.begin(); it!=m_tableVarps.end(); ++it) { + *it = findDuplicateTable(*it); + } + + createOutputAssigns(nodep, stmtsp, indexVscp, chgVscp); + + // Link it in. + if (AstAlways* nodeap = nodep->castAlways()) { + // Keep sensitivity list, but delete all else + nodeap->bodysp()->unlinkFrBack()->deleteTree(); + nodeap->addStmtp(stmtsp); + if (debug()>=6) nodeap->dumpTree(cout," table_new: "); + } else { + nodep->v3fatalSrc("Creating table under unknown node type"); + } + + // Cleanup internal structures + m_tableVarps.clear(); + } + + void createTableVars(AstNode* nodep) { + // Create table for each output + for (deque::iterator it = m_outVarps.begin(); it!=m_outVarps.end(); ++it) { + AstVarScope* outvscp = *it; + AstVar* outvarp = outvscp->varp(); + AstVar* tablevarp + = new AstVar (nodep->fileline(), AstVarType::MODULETEMP, + "__Vtable_" + cvtToStr(m_modTables) +"_"+outvarp->name(), + new AstRange (nodep->fileline(), outvarp->widthMin()-1, 0), + new AstRange (nodep->fileline(), VL_MASK_I(m_inWidth), 0)); + tablevarp->isConst(true); + tablevarp->isStatic(true); + tablevarp->initp(new AstInitArray (nodep->fileline(), NULL)); + m_modp->addStmtp(tablevarp); + AstVarScope* tablevscp = new AstVarScope(tablevarp->fileline(), m_scopep, tablevarp); + m_scopep->addVarp(tablevscp); + m_tableVarps.push_back(tablevscp); + } + } + + AstNode* createLookupInput(AstNode* nodep, AstVarScope* indexVscp) { + // Concat inputs into a single temp variable (inside always) + // First var in inVars becomes the LSB of the concat + AstNode* concatp = NULL; + for (deque::iterator it = m_inVarps.begin(); it!=m_inVarps.end(); ++it) { + AstVarScope* invscp = *it; + AstVarRef* refp = new AstVarRef (nodep->fileline(), invscp, false); + if (concatp) { + concatp = new AstConcat (nodep->fileline(), refp, concatp); + } else concatp = refp; + } + + AstNode* stmtsp = new AstAssign + (nodep->fileline(), + new AstVarRef (nodep->fileline(), indexVscp, true), + concatp); + return stmtsp; + } + + void createTableValues(AstAlways* nodep, AstVarScope* chgVscp) { + // Create table + // There may be a simulation path by which the output doesn't change value. + // We could bail on these cases, or we can have a "change it" boolean. + // We've choosen the later route, since recirc is common in large FSMs. + for (deque::iterator it = m_outVarps.begin(); it!=m_outVarps.end(); ++it) { + m_outNotSet.push_back(false); + } + uint32_t inValueNextInitArray=0; + TableSimulateVisitor simvis (this, false); + for (uint32_t inValue=0; inValue <= VL_MASK_I(m_inWidth); inValue++) { + // Make a new simulation structure so we can set new input values + UINFO(8," Simulating "<::iterator it = m_inVarps.begin(); it!=m_inVarps.end(); ++it) { + AstVarScope* invscp = *it; + // LSB is first variable, so extract it that way + simvis.newNumber(invscp, VL_MASK_I(invscp->width()) & (inValue>>shift)); + shift += invscp->width(); + // We're just using32 bit arithmetic, because there's no way the input table can be 2^32 bytes! + if (shift>31) nodep->v3fatalSrc("shift overflow"); + UINFO(8," Input "<name()<<" = "<<*(simvis.fetchNumber(invscp))<fileline(), m_outVarps.size(), 0); + for (deque::iterator it = m_outVarps.begin(); it!=m_outVarps.end(); ++it) { + AstVarScope* outvscp = *it; + V3Number* outnump = simvis.fetchNumberNull(outvscp); + AstNode* setp; + if (!outnump) { + UINFO(8," Output "<name()<<" never set\n"); + m_outNotSet[outnum] = true; + // Value in table is arbitrary, but we need something + setp = new AstConst (outvscp->fileline(), + V3Number(outvscp->fileline(), outvscp->width(), 0)); + } else { + UINFO(8," Output "<name()<<" = "<<*outnump<fileline(), *outnump); + } + // Note InitArray requires us to have the values in inValue order + m_tableVarps[outnum]->varp()->initp()->castInitArray()->addInitsp(setp); + outnum++; + } + + { // Set changed table + if (inValue != inValueNextInitArray++) + nodep->v3fatalSrc("InitArray requires us to have the values in inValue order"); + AstNode* setp = new AstConst (nodep->fileline(), outputChgMask); + chgVscp->varp()->initp()->castInitArray()->addInitsp(setp); + } + } // each value + } + + AstVarScope* findDuplicateTable(AstVarScope* vsc1p) { + // See if another table we've created is identical, if so use it for both. + AstVar* var1p = vsc1p->varp(); + for (deque::iterator it = m_modTableVscs.begin(); it!=m_modTableVscs.end(); ++it) { + AstVarScope* vsc2p= *it; + AstVar* var2p = vsc2p->varp(); + if (var1p->width() == var2p->width() + && var1p->arrayElements() == var2p->arrayElements()) { + AstNode* init1p = var1p->initp()->castInitArray(); + AstNode* init2p = var2p->initp()->castInitArray(); + if (init1p->sameTree(init2p)) { + UINFO(8," Duplicate table var "<iterateChildren(*this); + m_scopep = NULL; + } + virtual void visit(AstAlways* nodep, AstNUser*) { + UINFO(4," ALWAYS "<iterateChildren(*this); + } +public: + // CONSTRUCTORS + TableVisitor(AstNetlist* nodep) { + m_modp = NULL; + m_modTables = 0; + m_scopep = NULL; + m_assignDly = 0; + m_inWidth = 0; + m_outWidth = 0; + m_totalBytes = 0; + nodep->accept(*this); + } + virtual ~TableVisitor() { + V3Stats::addStat("Optimizations, Tables created", m_statTablesCre); + } +}; + +//###################################################################### + +void TableSimulateVisitor::varRefCb(AstVarRef* nodep) { + // Called by checking on each new varref encountered + // We cross-call into a TableVisitor function. + m_cbthis->simulateVarRefCb(nodep); +} + +//###################################################################### +// Table class functions + +void V3Table::tableAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include + +#include "V3Global.h" +#include "V3Task.h" +#include "V3Inst.h" +#include "V3Ast.h" + +//###################################################################### + +class TaskStateVisitor : public AstNVisitor { +private: + // NODE STATE + // Output: + // AstNodeFTask::user3p // AstScope* this FTask is under + + // TYPES + typedef std::map,AstVarScope*> VarToScopeMap; + // MEMBERS + VarToScopeMap m_varToScopeMap; // Map for Var -> VarScope mappings +public: + // METHODS + AstScope* getScope(AstNodeFTask* nodep) { + AstScope* scopep = nodep->user3p()->castNode()->castScope(); + if (!scopep) nodep->v3fatalSrc("No scope for function"); + return scopep; + } + AstVarScope* findVarScope(AstScope* scopep, AstVar* nodep) { + VarToScopeMap::iterator iter = m_varToScopeMap.find(make_pair(scopep,nodep)); + if (iter == m_varToScopeMap.end()) nodep->v3fatalSrc("No scope for var"); + return iter->second; + } +private: + // VISITORS + virtual void visit(AstScope* nodep, AstNUser*) { + // Each FTask is unique per-scope, so AstNodeFTaskRefs do not need + // pointers to what scope the FTask is to be invoked under. + // However, to create variables, we need to track the scopes involved. + // Find all var->varscope mappings, for later cleanup + for (AstNode* stmtp = nodep->varsp(); stmtp; stmtp=stmtp->nextp()) { + if (AstVarScope* vscp = stmtp->castVarScope()) { + if (vscp->varp()->isFuncLocal()) { + UINFO(9," funcvsc "<varp()), vscp)); + } + } + } + // Likewise, all FTask->scope mappings + for (AstNode* stmtp = nodep->blocksp(); stmtp; stmtp=stmtp->nextp()) { + if (AstNodeFTask* taskp = stmtp->castNodeFTask()) { + taskp->user3p(nodep); + } + } + // No iterateChildren for speed + } + //-------------------- + virtual void visit(AstNodeMath* nodep, AstNUser*) {} // Speedup + //-------------------- + // Default: Just iterate + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + TaskStateVisitor(AstNode* nodep) { + AstNode::user3ClearTree(); + nodep->iterateAndNext(*this, NULL); + } + virtual ~TaskStateVisitor() {} +}; + +//###################################################################### + +class TaskRelinkVisitor : public AstNVisitor { + // Replace varrefs with new var pointer +private: + // NODE STATE + // Input: + // AstVar::user2p // AstVarScope* to replace varref with + + // VISITORS + virtual void visit(AstVarRef* nodep, AstNUser*) { + // Similar code in V3Inline + if (nodep->varp()->user2p()) { // It's being converted to a alias. + UINFO(9, " relinkVar "<<(void*)nodep->varp()->user2p()<<" "<varp()->user2p()->castNode()->castVarScope(); + if (!newvscp) nodep->v3fatalSrc("Null?\n"); + nodep->varScopep(newvscp); + nodep->varp(nodep->varScopep()->varp()); + nodep->name(nodep->varp()->name()); + } + nodep->iterateChildren(*this); + } + + //-------------------- + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + TaskRelinkVisitor(AstNode* nodep) { + nodep->iterateAndNext(*this, NULL); + } + virtual ~TaskRelinkVisitor() {} +}; + +//###################################################################### +// Task state, as a visitor of each AstNode + +class TaskVisitor : public AstNVisitor { +private: + // NODE STATE + // Each module: + // AstNodeFTask::user // True if its been expanded + // Each funccall + // AstVar::user2p // AstVarScope* to replace varref with + // STATE + TaskStateVisitor* m_statep; // Common state between visitors + AstModule* m_modp; // Current module + AstScope* m_scopep; // Current scope + AstNode* m_lastStmtp; // Proceeding statement + int m_modNCalls; // Incrementing func # for making symbols + //int debug() { return 9; } + + // METHODS + AstVarScope* createVarScope(AstVar* invarp, const string& namePrefix) { + // We could create under either the ref's scope or the ftask's scope. + // It shouldn't matter, as they are only local variables. + // We choose to do it under whichever called this function, which results + // in more cache locality. + AstVar* newvarp = new AstVar (invarp->fileline(), AstVarType::BLOCKTEMP, + namePrefix+"__"+invarp->shortName(), invarp); + newvarp->funcLocal(false); + m_modp->addStmtp(newvarp); + AstVarScope* newvscp = new AstVarScope (newvarp->fileline(), m_scopep, newvarp); + m_scopep->addVarp(newvscp); + return newvscp; + } + + AstNode* createInlinedFTask(AstNodeFTaskRef* refp, string namePrefix, AstVarScope* outvscp) { + // outvscp is the variable for functions only, if NULL, it's a task + if (!refp->taskp()) refp->v3fatalSrc("Unlinked?"); + AstNode* newbodysp = refp->taskp()->stmtsp()->cloneTree(true); // Maybe NULL + AstNode* beginp = new AstComment(refp->fileline(), (string)("Function: ")+refp->name()); + if (newbodysp) beginp->addNext(newbodysp); + if (debug()>=9) { beginp->dumpTree(cout,"-newbody:"); } + // + // Create input variables + AstNode::user2ClearTree(); + AstNode* pinp = refp->pinsp(); + AstNode* nextpinp = pinp; + AstNode* nextstmtp; + for (AstNode* stmtp = newbodysp; stmtp; pinp=nextpinp, stmtp=nextstmtp) { + nextstmtp = stmtp->nextp(); + if (AstVar* portp = stmtp->castVar()) { + if (portp->isIO()) { + if (pinp==NULL) { + refp->v3error("Too few arguments in function call"); + pinp = new AstConst(refp->fileline(), 0); + m_modp->addStmtp(pinp); // For below unlink + } + UINFO(9, " Port "<nextp(); + pinp->unlinkFrBack(); // Relinked to assignment below + portp->unlinkFrBack(); // Remove it from the clone (not original) + pushDeletep(portp); + // + if (portp->isTristate()) { + refp->v3error("Unsupported: Inouts in functions/tasks"); + } + else if (portp->isOutput() && outvscp) { + refp->v3error("Outputs not allowed in function declarations"); + } + else if (portp->isOutput()) { + // Make output variables + // Correct lvalue; we didn't know when we linked + if (AstVarRef* varrefp = pinp->castVarRef()) { + varrefp->lvalue(true); + } else { + pinp->v3error("Unsupported: Task output pin connected to non-variable"); + } + // Even if it's referencing a varref, we still make a temporary + // Else task(x,x,x) might produce incorrect results + AstVarScope* outvscp = createVarScope (portp, namePrefix+"__"+portp->shortName()); + portp->user2p(outvscp); + AstAssign* assp = new AstAssign (pinp->fileline(), + pinp, + new AstVarRef(outvscp->fileline(), outvscp, false)); + // Put assignment BEHIND of all other statements + beginp->addNext(assp); + } + else if (portp->isInput()) { + // Make input variable + AstVarScope* inVscp = createVarScope (portp, namePrefix+"__"+portp->shortName()); + portp->user2p(inVscp); + AstAssign* assp = new AstAssign (pinp->fileline(), + new AstVarRef(inVscp->fileline(), inVscp, true), + pinp); + // Put assignment in FRONT of all other statements + if (AstNode* afterp = beginp->nextp()) { + afterp->unlinkFrBackWithNext(); + assp->addNext(afterp); + } + beginp->addNext(assp); + } + } + else { // Var is not I/O + // Move it to a new localized variable + AstVarScope* localVscp = createVarScope (portp, namePrefix+"__"+portp->shortName()); + portp->user2p(localVscp); + } + } + } + if (pinp!=NULL) refp->v3error("Too many arguments in function call"); + // Create function output variables + if (outvscp) { + //UINFO(0, "setflag on "<fvarp()<<" to "<taskp()->castFunc()->fvarp()->user2p(outvscp); + } + // Replace variable refs + TaskRelinkVisitor visit (beginp); + // + if (debug()>=9) { beginp->dumpTree(cout,"-iotask: "); } + return beginp; + } + + + AstCFunc* makeUserFunc(AstNodeFTask* nodep) { + // Given a already cloned node, make a public C function. + // Probably some of this work should be done later, but... + // should the type of the function be bool/uint32/64 etc (based on lookup) or IData? + AstNode::user2ClearTree(); + AstVar* rtnvarp = NULL; + AstVarScope* rtnvscp = NULL; + if (nodep->castFunc()) { + AstVar* portp = NULL; + if (NULL!=(portp = nodep->castFunc()->fvarp()->castVar())) { + if (!portp->isFuncReturn()) nodep->v3error("Not marked as function return var"); + if (portp->isWide()) nodep->v3error("Unsupported: Public functions/tasks with inputs or outputs > 64 bits wide."); + portp->unlinkFrBack(); + rtnvarp = portp; + rtnvarp->funcLocal(true); + rtnvscp = new AstVarScope (rtnvarp->fileline(), m_scopep, rtnvarp); + m_scopep->addVarp(rtnvscp); + rtnvarp->user2p(rtnvscp); + } else { + nodep->v3fatalSrc("function without function output variable"); + } + } + AstCFunc* funcp = new AstCFunc(nodep->fileline(), nodep->name(), + m_scopep, + (rtnvarp?rtnvarp->cType():"")); + if (rtnvarp) funcp->addArgsp(rtnvarp); + funcp->dontCombine(true); + funcp->funcPublic(true); + + // We need to get a pointer to all of our variables (may have eval'ed something else earlier) + funcp->addInitsp(new AstCStmt(nodep->fileline(), + string(" ")+v3Global.opt.prefix()+"__Syms::init(__VlSymsp);\n")); + + // Create list of arguments and move to function + for (AstNode* nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp=nextp) { + nextp = stmtp->nextp(); + if (AstVar* portp = stmtp->castVar()) { + if (portp->isIO()) { + // Move it to new function + portp->unlinkFrBack(); + portp->funcLocal(true); + funcp->addArgsp(portp); + if (portp->isWide()) { + // As we don't want to export our WData arrays to users, + // and casting to sc_bv's is ugly, we'll just... + nodep->v3error("Unsupported: Public functions/tasks with inputs or outputs > 64 bits wide."); + } + } else { + // "Normal" variable, mark inside function + portp->funcLocal(true); + } + AstVarScope* newvscp = new AstVarScope (portp->fileline(), m_scopep, portp); + m_scopep->addVarp(newvscp); + portp->user2p(newvscp); + } + } + // Move body + AstNode* bodysp = nodep->stmtsp(); + if (bodysp) { bodysp->unlinkFrBackWithNext(); funcp->addStmtsp(bodysp); } + // Return statement + if (rtnvscp) { + funcp->addFinalsp(new AstCReturn(rtnvscp->fileline(), + new AstVarRef(rtnvscp->fileline(), rtnvscp, false))); + } + // Replace variable refs + TaskRelinkVisitor visit (funcp); + // Delete rest of cloned task and return new func + pushDeletep(nodep); nodep=NULL; + if (debug()>=9) { funcp->dumpTree(cout,"-userFunc: "); } + return funcp; + } + + void iterateIntoFTask(AstNodeFTask* nodep) { + // Iterate into the FTask we are calling. Note it may be under a different + // scope then the caller, so we need to restore state. + AstScope* oldscopep = m_scopep; + m_scopep = m_statep->getScope(nodep); + nodep->accept(*this); + m_scopep = oldscopep; + } + + // VISITORS + virtual void visit(AstModule* nodep, AstNUser*) { + m_modp = nodep; + m_lastStmtp = NULL; + m_modNCalls = 0; + nodep->iterateChildren(*this); + } + virtual void visit(AstScope* nodep, AstNUser*) { + m_scopep = nodep; + m_lastStmtp = NULL; + nodep->iterateChildren(*this); + m_scopep = NULL; + } + virtual void visit(AstTaskRef* nodep, AstNUser*) { + iterateIntoFTask(nodep->taskp()); // First, do hierarchical funcs + UINFO(4," Task REF "<=9) { nodep->dumpTree(cout,"-inltask:"); } + // Create cloned statements + string namePrefix = "__Vtask_"+nodep->taskp()->shortName()+"__"+cvtToStr(m_modNCalls++); + AstNode* beginp = createInlinedFTask(nodep, namePrefix, NULL); + // Replace the ref + nodep->replaceWith(beginp); + nodep->deleteTree(); nodep=NULL; + } + virtual void visit(AstFuncRef* nodep, AstNUser*) { + UINFO(4," Func REF "<=9) { m_lastStmtp->dumpTree(cout,"-prestmt:"); } + // First, do hierarchical funcs + AstFunc* funcp = nodep->taskp()->castFunc(); + if (!funcp) nodep->v3fatalSrc("unlinked"); + AstNode* insertAtp = m_lastStmtp; + if (AstAssignW* awp = insertAtp->castAssignW()) { + // Wire assigns must become always statements to deal with insertion + // of multiple statements. Perhaps someday make all wassigns into always's? + AstNode* lhsp = awp->lhsp()->unlinkFrBack(); + AstNode* rhsp = awp->rhsp()->unlinkFrBack(); + AstNode* assignp = new AstAssign (awp->fileline(), lhsp, rhsp); + AstNode* alwaysp = new AstAlways (awp->fileline(), NULL, assignp); + m_lastStmtp = assignp; insertAtp = assignp; + awp->replaceWith(alwaysp); pushDeletep(awp); awp=NULL; + } + // Inline func refs in the function + iterateIntoFTask(funcp); + // Inline this reference + if (debug()>=9) { m_lastStmtp->dumpTree(cout,"-inlstmt:"); } + if (!insertAtp) nodep->v3fatalSrc("Function not underneath a statement"); + UINFO(5," Under "<shortName()+"__"+cvtToStr(m_modNCalls++); + AstVarScope* outvscp = createVarScope (funcp->fvarp()->castVar(), + namePrefix+"__out"); + // Create cloned statements + if (debug()>=9) { nodep->taskp()->dumpTree(cout,"-oldfunc:"); } + if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked?"); + + AstNode* beginp = createInlinedFTask(nodep, namePrefix, outvscp); + // Add the whole thing before insertAt + AstNRelinker handle; + insertAtp->unlinkFrBackWithNext(&handle); + if (debug()>=9) { beginp->dumpTree(cout,"-newfunc:"); } + beginp->addNext(insertAtp); + handle.relink(beginp); + // Replace the ref + AstVarRef* outrefp = new AstVarRef (nodep->fileline(), outvscp, false); + nodep->replaceWith(outrefp); + nodep->deleteTree(); nodep=NULL; + if (debug()>=9) { insertAtp->dumpTree(cout,"-newstmt:"); } + UINFO(4," Done.\n"); + } + virtual void visit(AstNodeFTask* nodep, AstNUser*) { + AstNode* prevLastStmtp = m_lastStmtp; + m_lastStmtp = nodep->stmtsp(); + if (!nodep->user()) { + // Expand functions in it & Mark for later delete + nodep->user(true); + if (!nodep->taskPublic()) { + nodep->unlinkFrBack(); + } else { + // Clone it first, because we may have later FTaskRef's that still need + // the original version. + AstNodeFTask* clonedFuncp = nodep->cloneTree(false)->castNodeFTask(); + AstCFunc* cfuncp = makeUserFunc(clonedFuncp); + // Replace it + nodep->replaceWith(cfuncp); + iterateIntoFTask(clonedFuncp); // Do the clone too + } + // Any variables inside the function still have varscopes pointing to them. + // We're going to delete the vars, so delete the varscopes. + if (nodep->castFunc()) { + if (AstVar* portp = nodep->castFunc()->fvarp()->castVar()) { + AstVarScope* vscp = m_statep->findVarScope(m_scopep, portp); + UINFO(9," funcremovevsc "<unlinkFrBack()); vscp=NULL; + } + } + for (AstNode* nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp=nextp) { + nextp = stmtp->nextp(); + if (AstVar* portp = stmtp->castVar()) { + AstVarScope* vscp = m_statep->findVarScope(m_scopep, portp); + UINFO(9," funcremovevsc "<unlinkFrBack()); vscp=NULL; + } + } + // Just push, as other references to func may remain until visitor exits + pushDeletep(nodep); nodep=NULL; + } + m_lastStmtp = prevLastStmtp; + } + virtual void visit(AstNodeStmt* nodep, AstNUser*) { + m_lastStmtp = nodep; + nodep->iterateChildren(*this); + } + //-------------------- + // Default: Just iterate + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + TaskVisitor(AstNetlist* nodep, TaskStateVisitor* statep) + : m_statep(statep) { + m_modp = NULL; + m_scopep = NULL; + AstNode::userClearTree(); + nodep->accept(*this); + } + virtual ~TaskVisitor() {} +}; + +//###################################################################### +// Task class functions + +void V3Task::taskAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "<varscope map +// Else, assign trace codes to each variable +// +//************************************************************************* + +#include "config.h" +#include +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3Trace.h" +#include "V3EmitCBase.h" +#include "V3Graph.h" +#include "V3Hashed.h" +#include "V3Stats.h" + +//###################################################################### +// Graph vertexes + +class TraceActivityVertex : public V3GraphVertex { + AstNode* m_insertp; // Insert before this statement + vlsint32_t m_activityCode; + bool m_activityCodeValid; + bool m_slow; // If always slow, we can use the same code +public: + TraceActivityVertex(V3Graph* graphp, AstNode* nodep, bool slow) + : V3GraphVertex(graphp), m_insertp(nodep) { + m_activityCode = 0; + m_activityCodeValid = false; + m_slow = slow; + } + enum { ACTIVITY_NEVER=(1UL<<31) }; + enum { ACTIVITY_ALWAYS=-1 }; + enum { ACTIVITY_SLOW=0 }; + class ActivityAlways {}; + TraceActivityVertex(V3Graph* graphp, vlsint32_t code) + : V3GraphVertex(graphp) { + activityCode(code); + m_slow = false; + } + virtual ~TraceActivityVertex() {} + // Accessors + AstNode* insertp() const { + if (!m_insertp) v3fatal("Null insertp; probably called on a special always/slow."); + return m_insertp; + } + virtual string name() const { + if (activityAlways()) return "*ALWAYS*"; + else return ((string)(slow()?"*SLOW* ":""))+insertp()->name(); + } + virtual string dotColor() const { return slow()?"yellowGreen":"green"; } + bool activityCodeValid() const { return m_activityCodeValid; } + vlsint32_t activityCode() const { return m_activityCode; } + bool activityAlways() const { return activityCode()==ACTIVITY_ALWAYS; } + void activityCode(vlsint32_t code) { m_activityCode=code; m_activityCodeValid=true;} + bool slow() const { return m_slow; } + void slow(bool flag) { if (!flag) m_slow=false; } +}; + +class TraceCFuncVertex : public V3GraphVertex { + AstCFunc* m_nodep; +public: + TraceCFuncVertex(V3Graph* graphp, AstCFunc* nodep) + : V3GraphVertex(graphp), m_nodep(nodep) { + } + virtual ~TraceCFuncVertex() {} + // Accessors + AstCFunc* nodep() const { return m_nodep; } + virtual string name() const { return nodep()->name(); } + virtual string dotColor() const { return "yellow"; } +}; + +class TraceTraceVertex : public V3GraphVertex { + AstTraceInc* m_nodep; // TRACEINC this represents + TraceTraceVertex* m_duplicatep; // NULL, or other vertex with the real code() that duplicates this one +public: + TraceTraceVertex(V3Graph* graphp, AstTraceInc* nodep) + : V3GraphVertex(graphp), m_nodep(nodep), m_duplicatep(NULL) {} + virtual ~TraceTraceVertex() {} + // Accessors + AstTraceInc* nodep() const { return m_nodep; } + virtual string name() const { return nodep()->declp()->name(); } + virtual string dotColor() const { return "red"; } + TraceTraceVertex* duplicatep() const { return m_duplicatep; } + void duplicatep(TraceTraceVertex* dupp) { + if (duplicatep()) nodep()->v3fatalSrc("Assigning duplicatep() to already duplicated node"); + m_duplicatep=dupp; } +}; + +class TraceVarVertex : public V3GraphVertex { + AstVarScope* m_nodep; +public: + TraceVarVertex(V3Graph* graphp, AstVarScope* nodep) + : V3GraphVertex(graphp), m_nodep(nodep) {} + virtual ~TraceVarVertex() {} + // Accessors + AstVarScope* nodep() const { return m_nodep; } + virtual string name() const { return nodep()->name(); } + virtual string dotColor() const { return "skyblue"; } +}; + +//###################################################################### +// Trace state, as a visitor of each AstNode + +class TraceVisitor : public EmitCBaseVisitor { +private: + // NODE STATE + // V3Hashed + // Ast*::user4() // V3Hashed calculation + // Cleared entire netlist + // AstCFunc::user() // V3GraphVertex* for this node + // AstTraceInc::user() // V3GraphVertex* for this node + // AstVarScope::user() // V3GraphVertex* for this node + // AstCCall::user2() // bool; walked next list for other ccalls + // Ast*::user3() // TraceActivityVertex* for this node + + // STATE + AstModule* m_topModp; // Module to add variables to + AstScope* m_highScopep; // Scope to add variables to + AstCFunc* m_funcp; // C function adding to graph + AstTraceInc* m_tracep; // Trace function adding to graph + AstCFunc* m_initFuncp; // Trace function we add statements to + AstCFunc* m_fullFuncp; // Trace function we add statements to + AstCFunc* m_chgFuncp; // Trace function we add statements to + AstVarScope* m_activityVscp; // Activity variable + uint32_t m_code; // Trace ident code# being assigned + V3Graph m_graph; // Var/CFunc tracking + TraceActivityVertex* m_alwaysVtxp; // "Always trace" vertex + bool m_finding; // Pass one of algorithm? + + V3Double0 m_statChgSigs; // Statistic tracking + V3Double0 m_statUniqSigs; // Statistic tracking + V3Double0 m_statUniqCodes;// Statistic tracking + //int debug() { return 9; } + + // METHODS + void detectDuplicates() { + UINFO(9,"Finding duplicates\n"); + // Note uses user4 + V3Hashed hashed; // Duplicate code detection + // Hash all of the values the traceIncs need + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { + AstTraceInc* nodep = vvertexp->nodep(); + if (nodep->valuep()) { + if (nodep->valuep()->backp() != nodep) nodep->v3fatalSrc("Trace duplicate back needs consistency, so we can map duplicates back to TRACEINCs"); + hashed.hashAndInsert(nodep->valuep()); + UINFO(8, " Hashed "<valuep()->user4()<<" "<verticesNextp()) { + if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { + AstTraceInc* nodep = vvertexp->nodep(); + if (nodep->valuep() && !vvertexp->duplicatep()) { + V3Hashed::iterator dupit = hashed.findDuplicate(nodep->valuep()); + if (dupit != hashed.end()) { + AstTraceInc* dupincp = hashed.iteratorNodep(dupit)->backp()->castTraceInc(); + if (!dupincp) nodep->v3fatalSrc("Trace duplicate of wrong type"); + TraceTraceVertex* dupvertexp = dynamic_cast(dupincp->userp()->castGraphVertex()); + UINFO(8," Orig "<duplicatep(vvertexp); + // Remove node from comparison so don't hit it again + hashed.erase(dupit); + } + } + } + } + AstNode::user4ClearTree(); + } + + void graphSimplify() { + // Remove all variable nodes + for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) { + nextp = itp->verticesNextp(); + if (TraceVarVertex* vvertexp = dynamic_cast(itp)) { + vvertexp->rerouteEdges(&m_graph); + vvertexp->unlinkDelete(&m_graph); + } + } + // Remove multiple variables connecting funcs to traces + // We do this twice, as then we have fewer edges to multiply out in the below expansion. + m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); + // Remove all Cfunc nodes + for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) { + nextp = itp->verticesNextp(); + if (TraceCFuncVertex* vvertexp = dynamic_cast(itp)) { + vvertexp->rerouteEdges(&m_graph); + vvertexp->unlinkDelete(&m_graph); + } + } + + // Remove multiple variables connecting funcs to traces + m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); + + // If there are any edges from a always, keep only the always + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { + V3GraphEdge* alwaysEdgep = NULL; + for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { + TraceActivityVertex* actVtxp = dynamic_cast(edgep->fromp()); + if (!actVtxp) vvertexp->nodep()->v3fatalSrc("Tracing a node with FROM non activity"); + if (actVtxp->activityAlways()) { + alwaysEdgep = edgep; + break; + } + } + if (alwaysEdgep) { + for (V3GraphEdge* nextp, *edgep = vvertexp->inBeginp(); edgep; edgep=nextp) { + nextp = edgep->inNextp(); + if (edgep!=alwaysEdgep) edgep->unlinkDelete(); + } + } + } + } + + // Activity points with no outputs can be removed + for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) { + nextp = itp->verticesNextp(); + if (TraceActivityVertex* vvertexp = dynamic_cast(itp)) { + if (!vvertexp->outBeginp()) { + vvertexp->unlinkDelete(&m_graph); + } + } + } + } + + void assignActivity() { + // Select activity numbers and put into each CFunc vertex + uint32_t activityNumber = 1; // Note 0 indicates "slow" + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (TraceActivityVertex* vvertexp = dynamic_cast(itp)) { + if (!vvertexp->activityCodeValid()) { + if (vvertexp->slow()) { + // If all functions in the calls are slow, we'll share the same code. + // This makes us need less activityNumbers and so speeds up the fast path. + vvertexp->activityCode(TraceActivityVertex::ACTIVITY_SLOW); + } else { + vvertexp->activityCode(activityNumber++); + } + } + } + } + + // Insert global variable + if (!activityNumber) activityNumber++; // For simplicity, always create it + activityNumber = VL_WORDS_I(activityNumber)*VL_WORDSIZE; // For tighter code; round to next 32 bit point. + AstVar* newvarp = new AstVar (m_chgFuncp->fileline(), AstVarType::MODULETEMP, + "__Vm_traceActivity", + new AstRange (m_chgFuncp->fileline(), activityNumber-1, 0)); + m_topModp->addStmtp(newvarp); + AstVarScope* newvscp = new AstVarScope(newvarp->fileline(), m_highScopep, newvarp); + m_highScopep->addVarp(newvscp); + m_activityVscp = newvscp; + + // Insert activity setter + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (TraceActivityVertex* vvertexp = dynamic_cast(itp)) { + if (!vvertexp->activityAlways()) { + FileLine* fl = vvertexp->insertp()->fileline(); + uint32_t acode = vvertexp->activityCode(); + vvertexp->insertp()->addNextHere + (new AstAssign (fl, + new AstSel (fl, new AstVarRef(fl, m_activityVscp, true), + new AstConst(fl, acode), + new AstConst(fl, 1)), + new AstConst (fl, V3Number(fl, 1, 1)))); + } + } + } + } + + void putTracesIntoTree() { + // Form a sorted list of the traces we are interested in + UINFO(9,"Making trees\n"); + + typedef set ActCodeSet; // All activity numbers applying to a given trace + typedef multimap TraceVec; // For activity set, what traces apply + TraceVec traces; + + // Form sort structure + // If a trace doesn't have activity, it's constant, and we don't need to track changes on it. + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { + ActCodeSet actset; + UINFO(9," Add to sort: "<=9) vvertexp->nodep()->dumpTree(cout,"- trnode: "); + for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { + TraceActivityVertex* cfvertexp = dynamic_cast(edgep->fromp()); + if (!cfvertexp) vvertexp->nodep()->v3fatalSrc("Should have been function pointing to this trace"); + UINFO(9," Activity: "<activityAlways()) { + // If code 0, we always trace; ignore other codes + actset.clear(); + actset.insert(TraceActivityVertex::ACTIVITY_ALWAYS); + break; + } else { + uint32_t acode = cfvertexp->activityCode(); + if (actset.find(acode) == actset.end()) { + actset.insert(acode); + } + } + } + // If a trace doesn't have activity, it's constant, and we don't need to track changes on it. + // We put constants and non-changers last, as then the prevvalue vector is more compacted + if (actset.empty()) actset.insert(TraceActivityVertex::ACTIVITY_NEVER); + traces.insert(make_pair(actset, vvertexp)); + } + } + + // Our keys are now sorted to have same activity number adjacent, + // then by trace order. (Better would be execution order for cache efficiency....) + // Last are constants and non-changers, as then the last value vector is more compact + + // Put TRACEs back into the tree + const ActCodeSet* lastactp = NULL; + AstNode* lastnodep = NULL; + for (TraceVec::iterator it = traces.begin(); it!=traces.end(); ++it) { + const ActCodeSet& actset = it->first; + TraceTraceVertex* vvertexp = it->second; + UINFO(9," Done sort: "<nodep(), needChg); + if (addp) { // Else no activity or duplicate + if (actset.find(TraceActivityVertex::ACTIVITY_NEVER) != actset.end()) { + vvertexp->nodep()->v3fatalSrc("If never, needChg=0 and shouldn't need to add."); + } else if (actset.find(TraceActivityVertex::ACTIVITY_ALWAYS) != actset.end()) { + // Must always set it; add to base of function + m_chgFuncp->addStmtsp(addp); + } else if (lastactp && actset == *lastactp && lastnodep) { + // Add to last statement we built + lastnodep->addNext(addp); + lastnodep = addp; + } else { + // Build a new IF statement + FileLine* fl = addp->fileline(); + AstNode* condp = NULL; + for (ActCodeSet::iterator csit = actset.begin(); csit!=actset.end(); ++csit) { + uint32_t acode = *csit; + AstNode* selp = new AstSel (fl, new AstVarRef(fl, m_activityVscp, false), + new AstConst(fl, acode), + new AstConst(fl, 1)); + if (condp) condp = new AstOr (fl, condp, selp); + else condp = selp; + } + AstIf* ifp = new AstIf (fl, condp, addp, NULL); + ifp->branchPred(AstBranchPred::UNLIKELY); + m_chgFuncp->addStmtsp(ifp); + lastactp = &actset; + lastnodep = addp; + } + } + } + + // Set in initializer + + // Clear activity after tracing completes + FileLine* fl = m_chgFuncp->fileline(); + AstNode* clrp = new AstAssign (fl, + new AstVarRef(fl, m_activityVscp, true), + new AstConst(fl, V3Number(fl, m_activityVscp->width()))); + m_fullFuncp->addFinalsp(clrp->cloneTree(true)); + m_chgFuncp->addFinalsp(clrp); + } + + uint32_t assignDeclCode(AstTraceDecl* nodep) { + if (!nodep->code()) { + nodep->code(m_code); + m_code += nodep->codeInc(); + m_statUniqCodes += nodep->codeInc(); + m_statUniqSigs++; + } + return nodep->code(); + } + + AstNode* assignTraceCode(TraceTraceVertex* vvertexp, AstTraceInc* nodep, bool needChg) { + // Assign trace code, add to tree, return node for change tree or null + // Look for identical copies + uint32_t codePreassigned = 0; + //if (debug()>=9) nodep->dumpTree(cout,"- assnnode: "); + // Find non-duplicated node; note some nodep's maybe null, as they were deleted below + TraceTraceVertex* dupvertexp = vvertexp; + while (dupvertexp->duplicatep()) { + dupvertexp = dupvertexp->duplicatep(); + UINFO(9," dupOf "<<((void*)dupvertexp)<<" "<<((void*)dupvertexp->nodep()) + <<" "<nodep()->declp()); + nodep->declp()->code(codePreassigned); + } else { + assignDeclCode(nodep->declp()); + } + UINFO(8," Created code="<declp()->code() + <<" "<<(codePreassigned?"[PREASS]":"") + <<" "<<(needChg?"[CHG]":"")<<" "<cloneTree(true); + } + m_fullFuncp->addStmtsp(nodep); + } else { + // Duplicates don't need a TraceInc + pushDeletep(nodep); nodep=NULL; + } + return incAddp; + } + + TraceCFuncVertex* getCFuncVertexp(AstCFunc* nodep) { + TraceCFuncVertex* vertexp = dynamic_cast(nodep->userp()->castGraphVertex()); + if (!vertexp) { + vertexp = new TraceCFuncVertex(&m_graph, nodep); + nodep->userp(vertexp); + } + return vertexp; + } + TraceActivityVertex* getActivityVertexp(AstNode* nodep, bool slow) { + TraceActivityVertex* vertexp = dynamic_cast(nodep->user3p()->castGraphVertex()); + if (!vertexp) { + vertexp = new TraceActivityVertex(&m_graph, nodep, slow); + nodep->user3p(vertexp); + } + vertexp->slow(slow); + return vertexp; + } + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + AstNode::userClearTree(); + AstNode::user2ClearTree(); + AstNode::user3ClearTree(); + m_code = 1; // Multiple TopScopes will require fixing how code#s + // are assigned as duplicate varscopes must result in the same tracing code#. + + // Make a always vertex + m_alwaysVtxp = new TraceActivityVertex(&m_graph, TraceActivityVertex::ACTIVITY_ALWAYS); + + // Add vertexes for all TRACES, and edges from VARs each trace looks at + m_finding = false; + nodep->iterateChildren(*this); + + // Add vertexes for all CFUNCs, and edges to VARs the func sets + m_finding = true; + nodep->iterateChildren(*this); + m_finding = false; + + // Detect and remove duplicate values + detectDuplicates(); + + // Simplify it + if (debug()>=6) m_graph.dumpDotFilePrefixed("trace_pre"); + graphSimplify(); + if (debug()>=6) m_graph.dumpDotFilePrefixed("trace_opt"); + + // Create new TRACEINCs + assignActivity(); + putTracesIntoTree(); + } + virtual void visit(AstModule* nodep, AstNUser*) { + if (nodep->isTop()) m_topModp = nodep; + nodep->iterateChildren(*this); + } + virtual void visit(AstTopScope* nodep, AstNUser*) { + AstScope* scopep = nodep->scopep()->castScope(); + if (!scopep) nodep->v3fatalSrc("No scope found on top level"); + m_highScopep = scopep; + nodep->iterateChildren(*this); + } + virtual void visit(AstCCall* nodep, AstNUser*) { + UINFO(8," CCALL "<user2()) { + // See if there are other calls in same statement list; + // If so, all funcs might share the same activity code + TraceActivityVertex* activityVtxp = getActivityVertexp(nodep, nodep->funcp()->slow()); + for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { + if (AstCCall* ccallp = nextp->castCCall()) { + ccallp->user2(true); // Processed + UINFO(8," SubCCALL "<funcp()); + activityVtxp->slow(ccallp->funcp()->slow()); + new V3GraphEdge (&m_graph, activityVtxp, ccallFuncVtxp, 1); + } + } + } + nodep->iterateChildren(*this); + } + virtual void visit(AstCFunc* nodep, AstNUser*) { + UINFO(8," CFUNC "<funcType() == AstCFuncType::TRACE_INIT) { + m_initFuncp = nodep; + } else if (nodep->funcType() == AstCFuncType::TRACE_FULL) { + m_fullFuncp = nodep; + } else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) { + m_chgFuncp = nodep; + } + V3GraphVertex* funcVtxp = new TraceCFuncVertex(&m_graph, nodep); + if (!m_finding) { // If public, we need a unique activity code to allow for sets directly in this func + if (nodep->funcPublic() || nodep->name() == "_eval") { + // Need a non-null place to remember to later add a statement; make one + if (!nodep->stmtsp()) nodep->addStmtsp(new AstComment(nodep->fileline(), "Tracing activity check")); + V3GraphVertex* activityVtxp = getActivityVertexp(nodep->stmtsp(), nodep->slow()); + new V3GraphEdge (&m_graph, activityVtxp, funcVtxp, 1); + } + } + m_funcp = nodep; + nodep->iterateChildren(*this); + m_funcp = NULL; + } + virtual void visit(AstTraceInc* nodep, AstNUser*) { + UINFO(8," TRACE "<v3fatalSrc("Traces should have been removed in prev step."); + nodep->unlinkFrBack(); + + V3GraphVertex* vertexp = new TraceTraceVertex(&m_graph, nodep); + nodep->userp(vertexp); + + if (!m_funcp || (!m_chgFuncp || !m_fullFuncp)) nodep->v3fatalSrc("Trace not under func"); + m_tracep = nodep; + nodep->iterateChildren(*this); + m_tracep = NULL; + } + virtual void visit(AstVarRef* nodep, AstNUser*) { + if (m_tracep) { + if (!nodep->varScopep()) nodep->v3fatalSrc("No var scope?"); + if (nodep->lvalue()) nodep->v3fatalSrc("Lvalue in trace? Should be const."); + V3GraphVertex* varVtxp = nodep->varScopep()->userp()->castGraphVertex(); + if (!varVtxp) { + varVtxp = new TraceVarVertex(&m_graph, nodep->varScopep()); + nodep->varScopep()->userp(varVtxp); + } + V3GraphVertex* traceVtxp = m_tracep->userp()->castGraphVertex(); + new V3GraphEdge(&m_graph, varVtxp, traceVtxp, 1); + if (nodep->varp()->isPrimaryIn()) { // Always need to trace primary inputs + new V3GraphEdge(&m_graph, m_alwaysVtxp, traceVtxp, 1); + } + } + else if (m_funcp && m_finding && nodep->lvalue()) { + if (!nodep->varScopep()) nodep->v3fatalSrc("No var scope?"); + V3GraphVertex* funcVtxp = getCFuncVertexp(m_funcp); + V3GraphVertex* varVtxp = nodep->varScopep()->userp()->castGraphVertex(); + if (varVtxp) { // else we're not tracing this signal + new V3GraphEdge(&m_graph, funcVtxp, varVtxp, 1); + } + } + } + //-------------------- + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + TraceVisitor(AstNetlist* nodep) { + m_funcp = NULL; + m_tracep = NULL; + m_topModp = NULL; + m_highScopep = NULL; + m_finding = false; + m_activityVscp = NULL; + m_alwaysVtxp = NULL; + m_initFuncp = NULL; + m_fullFuncp = NULL; + m_chgFuncp = NULL; + nodep->accept(*this); + } + virtual ~TraceVisitor() { + V3Stats::addStat("Tracing, Unique changing signals", m_statChgSigs); + V3Stats::addStat("Tracing, Unique traced signals", m_statUniqSigs); + V3Stats::addStat("Tracing, Unique trace codes", m_statUniqCodes); + } +}; + +//###################################################################### +// Trace class functions + +void V3Trace::traceAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include + +#include "V3Global.h" +#include "V3TraceDecl.h" +#include "V3EmitCBase.h" +#include "V3Stats.h" + +//###################################################################### +// TraceDecl state, as a visitor of each AstNode + +class TraceDeclVisitor : public EmitCBaseVisitor { +private: + // NODE STATE + // Cleared entire netlist + + // STATE + AstModule* m_modp; // Current module + AstScope* m_scopetopp; // Current top scope + AstCFunc* m_initFuncp; // Trace function being built + AstCFunc* m_fullFuncp; // Trace function being built + AstCFunc* m_chgFuncp; // Trace function being built + V3Double0 m_statSigs; // Statistic tracking + V3Double0 m_statIgnSigs; // Statistic tracking + //int debug() { return 9; } + + // METHODS + const char* varIgnoreTrace(AstVar* nodep) { + // Return true if this shouldn't be traced + string prettyName = nodep->prettyName(); + if (!nodep->isTrace()) + return "Verilator trace_off"; + if (prettyName.c_str()[0] == '_') + return "Leading underscore"; + if (prettyName.find("._") != string::npos) + return "Inlined leading underscore"; + if (nodep->width() > 256) return "Wide bus > 256 bits"; + if (nodep->arrayElements() > 32) return "Wide memory > 32 ents"; + if (nodep->arrayp(1)) return "Unsupported: Multi-dimensional array"; + return NULL; + } + + // VISITORS + virtual void visit(AstTopScope* nodep, AstNUser*) { + m_scopetopp = nodep->scopep()->castScope(); + // The container for m_traceFuncp must be made first + { + AstCFunc* funcp = new AstCFunc(nodep->fileline(), "traceInitThis", m_scopetopp); + funcp->slow(true); + funcp->argTypes("SpTraceVcd* vcdp, uint32_t code"); + funcp->funcType(AstCFuncType::TRACE_INIT); + m_scopetopp->addActivep(funcp); + m_initFuncp = funcp; + UINFO(5," Newfunc "<fileline(), "traceFullThis", m_scopetopp); + funcp->slow(true); + funcp->argTypes("SpTraceVcd* vcdp, uint32_t code"); + funcp->funcType(AstCFuncType::TRACE_FULL); + m_scopetopp->addActivep(funcp); + m_fullFuncp = funcp; + } + { + AstCFunc* funcp = new AstCFunc(nodep->fileline(), "traceChgThis", m_scopetopp); + funcp->argTypes("SpTraceVcd* vcdp, uint32_t code"); + funcp->funcType(AstCFuncType::TRACE_CHANGE); + m_scopetopp->addActivep(funcp); + m_chgFuncp = funcp; + } + nodep->iterateChildren(*this); + } + virtual void visit(AstVarScope* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (!nodep->varp()->isTemp() && !nodep->varp()->isParam() && !nodep->varp()->isFuncLocal()) { + UINFO(5, " vsc "<varp(); + AstScope* scopep = nodep->scopep(); + // Compute show name + string showname = scopep->prettyName() + "." + varp->prettyName(); + if (showname.substr(0,4) == "TOP.") showname.replace(0,4,""); + if (!m_initFuncp) nodep->v3fatalSrc("NULL"); + if (varIgnoreTrace(varp)) { + m_statIgnSigs++; + m_initFuncp->addStmtsp( + new AstComment(nodep->fileline(), + "Tracing: "+showname+" // Ignored: "+varIgnoreTrace(varp))); + } else { + m_statSigs++; + AstNode* valuep = NULL; + if (nodep->valuep()) valuep=nodep->valuep()->cloneTree(true); + else valuep = new AstVarRef(nodep->fileline(), nodep, false); + AstTraceDecl* declp = new AstTraceDecl(nodep->fileline(), showname, varp); + m_initFuncp->addStmtsp(declp); + m_chgFuncp->addStmtsp(new AstTraceInc(nodep->fileline(), declp, valuep)); + // The full version will get constructed in V3Trace + } + } + } + //-------------------- + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + TraceDeclVisitor(AstNetlist* nodep) { + m_scopetopp = NULL; + m_initFuncp = NULL; + m_fullFuncp = NULL; + m_chgFuncp = NULL; + nodep->accept(*this); + } + virtual ~TraceDeclVisitor() { + V3Stats::addStat("Tracing, Traced signals", m_statSigs); + V3Stats::addStat("Tracing, Ignored signals", m_statIgnSigs); + } +}; + +//###################################################################### +// Trace class functions + +void V3TraceDecl::traceDeclAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< 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 +#include +#include +#include + +#include "V3Global.h" +#include "V3Unknown.h" +#include "V3Ast.h" +#include "V3Const.h" +#include "V3Stats.h" + +//###################################################################### + +class UnknownVisitor : public AstNVisitor { +private: + // NODE STATE + // Cleared on Netlist + // AstArraySel::user() -> bool. Set true if already processed + + // STATE + AstModule* m_modp; // Current module + bool m_constXCvt; // Convert X's + V3Double0 m_statUnkVars; // Statistic tracking + + //int debug() { return 9; } + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + //VV***** We reset all userp() on the netlist!!! + AstNode::userClearTree(); + nodep->iterateChildren(*this); + } + virtual void visit(AstModule* nodep, AstNUser*) { + UINFO(4," MOD "<iterateChildren(*this); + } + virtual void visit(AstCaseItem* nodep, AstNUser*) { + m_constXCvt = false; // Avoid loosing the X's in casex + nodep->condsp()->iterateAndNext(*this); + m_constXCvt = true; + nodep->bodysp()->iterateAndNext(*this); + } + void visitEqNeqCase(AstNodeBiop* nodep) { + UINFO(4," N/EQCASE->EQ "<lhsp()); + V3Const::constifyTree(nodep->rhsp()); + if (nodep->lhsp()->castConst() && nodep->rhsp()->castConst()) { + // Both sides are constant, node can be constant + V3Const::constifyTree(nodep); nodep=NULL; + return; + } else { + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* newp; + // If we got ==1'bx it can never be true (but 1'bx==1'bx can be!) + if (((lhsp->castConst() && lhsp->castConst()->num().isFourState()) + || (rhsp->castConst() && rhsp->castConst()->num().isFourState()))) { + V3Number num (nodep->fileline(), 1, (nodep->castEqCase()?0:1)); + newp = new AstConst (nodep->fileline(), num); + } else { + if (nodep->castEqCase()) + newp = new AstEq (nodep->fileline(), lhsp, rhsp); + else newp = new AstNeq (nodep->fileline(), lhsp, rhsp); + } + nodep->replaceWith(newp); + nodep->deleteTree(); nodep=NULL; + // Iterate tree now that we may have gotten rid of Xs + newp->iterateChildren(*this); + } + } + + virtual void visit(AstEqCase* nodep, AstNUser*) { + visitEqNeqCase(nodep); + } + virtual void visit(AstNeqCase* nodep, AstNUser*) { + visitEqNeqCase(nodep); + } + virtual void visit(AstIsUnknown* nodep, AstNUser*) { + nodep->iterateChildren(*this); + // Ahh, we're two state, so this is easy + UINFO(4," ISUNKNOWN->0 "<fileline(), 1, 0); + AstConst* newp = new AstConst (nodep->fileline(), zero); + nodep->replaceWith(newp); + nodep->deleteTree(); nodep=NULL; + } + virtual void visit(AstConst* nodep, AstNUser*) { + if (m_constXCvt + && nodep->num().isFourState()) { + UINFO(4," CONST4 "<=9) nodep->dumpTree(cout," Const_old: "); + // CONST(num) -> VARREF(newvarp) + // -> VAR(newvarp) + // -> INITIAL(VARREF(newvarp, OR(num_No_Xs,AND(random,num_1s_Where_X)) + V3Number numb1 (nodep->fileline(), nodep->width()); + numb1.opBitsOne(nodep->num()); + V3Number numbx (nodep->fileline(), nodep->width()); + numbx.opBitsXZ(nodep->num()); + if (v3Global.opt.xAssign()!="unique") { + // All X bits just become 0; fastest simulation, but not nice + V3Number numnew (nodep->fileline(), numb1.width()); + if (v3Global.opt.xAssign()=="1") { + numnew.opOr(numb1, numbx); + } else { + numnew.opAssign(numb1); + } + AstConst* newp = new AstConst(nodep->fileline(), numnew); + nodep->replaceWith(newp); + nodep->deleteTree(); nodep=NULL; + UINFO(4," -> "<varNumGetInc())); + AstVar* newvarp + = new AstVar (nodep->fileline(), AstVarType::MODULETEMP, newvarname, + new AstRange(nodep->fileline(), nodep->width()-1, 0)); + m_statUnkVars++; + AstNRelinker replaceHandle; + nodep->unlinkFrBack(&replaceHandle); + AstNodeVarRef* newref1p = new AstVarRef(nodep->fileline(), newvarp, false); + replaceHandle.relink(newref1p); // Replace const with varref + AstInitial* newinitp + = new AstInitial( + nodep->fileline(), + new AstAssign( + nodep->fileline(), + new AstVarRef(nodep->fileline(), newvarp, true), + new AstOr(nodep->fileline(), + new AstConst(nodep->fileline(),numb1), + new AstAnd(nodep->fileline(), + new AstConst(nodep->fileline(),numbx), + new AstRand(nodep->fileline(), + nodep->width()))))); + // Add inits in front of other statement. + // In the future, we should stuff the initp into the module's constructor. + AstNode* afterp = m_modp->stmtsp()->unlinkFrBackWithNext(); + m_modp->addStmtp(newvarp); + m_modp->addStmtp(newinitp); + m_modp->addStmtp(afterp); + if (debug()>=9) newref1p->dumpTree(cout," _new: "); + if (debug()>=9) newvarp->dumpTree(cout," _new: "); + if (debug()>=9) newinitp->dumpTree(cout," _new: "); + } + } + } + + void visit(AstSel* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (!nodep->user()) { + nodep->user(1); + // Guard against reading/writing past end of bit vector array + int maxmsb = 0; + bool lvalue = false; + AstNode* basefromp = AstArraySel::baseFromp(nodep->fromp()); + if (AstNodeVarRef* varrefp = basefromp->castNodeVarRef()) { + lvalue = varrefp->lvalue(); + maxmsb = (varrefp->varp()->width()-1); + } else { + // If it's a PARAMETER[bit], then basefromp may be a constant instead of a varrefp + maxmsb = basefromp->widthMin()-1; + } + int maxlsb = maxmsb - nodep->widthMin() + 1; + if (debug()>=9) nodep->dumpTree(cout,"sel_old: "); + V3Number maxlsbnum (nodep->fileline(), nodep->lsbp()->width(), maxlsb); + if (!lvalue) { + // SEL(...) -> COND(LTE(bit<=maxlsb), ARRAYSEL(...), {width{1'bx}}) + AstNRelinker replaceHandle; + nodep->unlinkFrBack(&replaceHandle); + V3Number xnum (nodep->fileline(), nodep->width()); + xnum.setAllBitsX(); + // Put the new nodes under a temporary XOR operator we'll rip up in a moment. + // This way when we constify, if the expression + // is a constant it will still be under the XOR. + AstXor* newp = new AstXor ( + nodep->fileline(), + new AstCondBound (nodep->fileline(), + new AstLte (nodep->fileline(), + nodep->lsbp()->cloneTree(false), + new AstConst(nodep->fileline(), maxlsbnum)), + nodep, + new AstConst(nodep->fileline(), xnum)), + new AstConst (nodep->fileline(), 0)); // Just so it's a valid XOR + if (debug()>=9) newp->dumpTree(cout," _new: "); + V3Const::constifyTree(newp->lhsp()); // Just the conditional + if (debug()>=9) newp->dumpTree(cout," _con: "); + // Link in conditional, can blow away temp xor + AstNode* nnp = newp->lhsp()->unlinkFrBack(); + replaceHandle.relink(nnp); nodep=NULL; + newp->deleteTree(); newp=NULL; + // Added X's, tristate them too + nnp->accept(*this); + } + else { + // SEL(...) -> SEL(COND(LTE(bit<=maxlsb), bit, 0)) + AstNRelinker replaceHandle; + AstNode* lsbp = nodep->lsbp()->unlinkFrBack(&replaceHandle); + V3Number zeronum (nodep->fileline(), lsbp->width(), 0); + AstCondBound* newp = new AstCondBound + (lsbp->fileline(), + new AstLte (lsbp->fileline(), + lsbp->cloneTree(false), + new AstConst(lsbp->fileline(), maxlsbnum)), + lsbp, + new AstConst(lsbp->fileline(), zeronum)); + replaceHandle.relink(newp); + // Added X's, tristate them too + if (debug()>=9) nodep->dumpTree(cout," _new: "); + V3Const::constifyTree(nodep); + if (debug()>=9) nodep->dumpTree(cout," _con: "); + nodep->iterateChildren(*this); + } + } + } + + virtual void visit(AstArraySel* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (!nodep->user()) { + nodep->user(1); + // Guard against reading/writing past end of arrays + AstNode* basefromp = AstArraySel::baseFromp(nodep->fromp()); + int dimension = AstArraySel::dimension(nodep->fromp()); + int maxmsb = 0; + bool lvalue = false; + if (AstNodeVarRef* varrefp = basefromp->castNodeVarRef()) { + lvalue = varrefp->lvalue(); + maxmsb = (varrefp->varp()->arrayp(dimension)->elementsConst()-1); + } else if (AstConst* lhconstp = basefromp->castConst()) { + // If it's a PARAMETER[bit], then basefromp may be a constant instead of a varrefp + maxmsb = lhconstp->widthMin(); + } else { + nodep->v3fatalSrc("No VarRef or Const under ArraySel\n"); + } + if (debug()>=9) nodep->dumpTree(cout,"arraysel_old: "); + V3Number widthnum (nodep->fileline(), nodep->bitp()->width(), maxmsb); + if (!lvalue + && !nodep->backp()->castArraySel()) { // Too complicated and slow if mid-multidimension + // ARRAYSEL(...) -> COND(LT(bitunlinkFrBack(&replaceHandle); + V3Number xnum (nodep->fileline(), nodep->width()); + xnum.setAllBitsX(); + // Put the new nodes under a temporary XOR operator we'll rip up in a moment. + // This way when we constify, if the expression + // is a constant it will still be under the XOR. + AstXor* newp = new AstXor ( + nodep->fileline(), + new AstCondBound (nodep->fileline(), + new AstLte (nodep->fileline(), + nodep->bitp()->cloneTree(false), + new AstConst(nodep->fileline(), widthnum)), + nodep, + new AstConst(nodep->fileline(), xnum)), + new AstConst (nodep->fileline(), 0)); // Just so it's a valid XOR + if (debug()>=9) newp->dumpTree(cout," _new: "); + V3Const::constifyTree(newp->lhsp()); // Just the conditional + if (debug()>=9) newp->dumpTree(cout," _con: "); + // Link in conditional, can blow away temp xor + AstNode* nnp = newp->lhsp()->unlinkFrBack(); + replaceHandle.relink(nnp); nodep=NULL; + newp->deleteTree(); newp=NULL; + // Added X's, tristate them too + nnp->accept(*this); + } + else { + // ARRAYSEL(...) -> ARRAYSEL(COND(LT(bitbitp()->unlinkFrBack(&replaceHandle); + V3Number zeronum (nodep->fileline(), bitp->width(), 0); + AstCondBound* newp = new AstCondBound + (bitp->fileline(), + new AstLte (bitp->fileline(), + bitp->cloneTree(false), + new AstConst(bitp->fileline(), widthnum)), + bitp, + new AstConst(bitp->fileline(), zeronum)); + replaceHandle.relink(newp); + // Added X's, tristate them too + if (debug()>=9) nodep->dumpTree(cout," _new: "); + V3Const::constifyTree(nodep); + if (debug()>=9) nodep->dumpTree(cout," _con: "); + nodep->iterateChildren(*this); + } + } + } + //-------------------- + // Default: Just iterate + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + UnknownVisitor(AstNode* nodep) { + nodep->accept(*this); + } + virtual ~UnknownVisitor() { + V3Stats::addStat("Unknowns, variables created", m_statUnkVars); + } +}; + +//###################################################################### +// Unknown class functions + +void V3Unknown::unknownAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include +#include + +#include "V3Global.h" +#include "V3Unroll.h" +#include "V3Stats.h" +#include "V3Const.h" +#include "V3Ast.h" + +//###################################################################### +// Unroll state, as a visitor of each AstNode + +class UnrollVisitor : public AstNVisitor { +private: + // STATE + AstVar* m_forVarp; // Iterator variable + AstVarScope* m_forVscp; // Iterator variable scope (NULL for generate pass) + AstConst* m_varValuep; // Current value of loop + bool m_varModeCheck; // Just checking RHS assignments + bool m_varModeReplace; // Replacing varrefs + bool m_varAssignHit; // Assign var hit + bool m_inBegin; // Inside a begin/end loop + bool m_generate; // Expand single generate For loop + V3Double0 m_statLoops; // Statistic tracking + V3Double0 m_statIters; // Statistic tracking + + //int debug() { return 9; } + + // VISITORS + virtual void visit(AstModule* nodep, AstNUser*) { + UINFO(4," MOD "<iterateChildren(*this); + } + + bool cantUnroll(AstNodeFor* nodep, const char* reason) { + if (m_generate) { + nodep->v3error("Unsupported: Can't unroll generate for; "<initsp()) V3Const::constifyTree(nodep->initsp()); // May change what is under init, leave here + V3Const::constifyTree(nodep->condp()); + if (nodep->assignsp()) V3Const::constifyTree(nodep->assignsp()); + + AstAssign* initp = nodep->initsp()->castAssign(); + if (!initp) nodep->v3fatalSrc("no initial assignment"); + m_forVarp = initp->lhsp()->castVarRef()->varp(); + m_forVscp = initp->lhsp()->castVarRef()->varScopep(); + if (nodep->castGenFor() && !m_forVarp->isGenVar()) { + nodep->v3error("Non-genvar used in generate for: "<name()<condp()->castNodeBiop(); + AstAssign* assignp = nodep->assignsp()->castAssign(); + AstNodeBiop* incInstrp = assignp->rhsp()->castNodeBiop(); + if (!assignp) nodep->v3fatalSrc("no increment assignment"); + UINFO(4, " FOR Check "<=9) nodep->dumpTree(cout," for: "); + // + // Extract the constant loop bounds + // To keep the IF levels low, we return as each test fails. + if (m_generate) V3Const::constifyParam(initp->rhsp()); + AstConst* constInitp = initp->rhsp()->castConst(); + if (!constInitp) return cantUnroll(nodep, "non-constant initializer"); + bool subtract = incInstrp->castSub(); + { + if (!subtract && !incInstrp->castAdd()) return cantUnroll(nodep, "missing add/sub for incrementer"); + AstVarRef* incVarrp = (subtract ? incInstrp->lhsp()->castVarRef() + : incInstrp->rhsp()->castVarRef()); + if (!incVarrp) return cantUnroll(nodep, "missing variable in incrementer"); + if (incVarrp->varp() != m_forVarp + || incVarrp->varScopep() != m_forVscp) { + return cantUnroll(nodep, "different variables in incrementer"); + } + } + // + // Adds have the # on the lhsp because V3Const pushes rhs consts over to the lhs + // Subtracts have it on the rhs, because you write i=i-1; i=1-i is non-sensible. + AstConst* preconstIncp = (subtract ? incInstrp->rhsp()->castConst() + : incInstrp->lhsp()->castConst()); + if (m_generate) V3Const::constifyParam(preconstIncp); + AstConst* constIncp = (subtract ? incInstrp->rhsp()->castConst() + : incInstrp->lhsp()->castConst()); + UINFO(8, " Inc expr ok: "<isZero()) return cantUnroll(nodep, "zero increment"); // Or we could loop forever below... + + bool lt = condp->castLt() || condp->castLtS(); + bool lte = condp->castLte() || condp->castLteS(); + bool gt = condp->castGt() || condp->castGtS(); + bool gte = condp->castGte() || condp->castGteS(); + if (!lt && !lte && !gt && !gte) + return cantUnroll(nodep, "condition not <= or <"); + if (!condp->lhsp()->castVarRef()) + return cantUnroll(nodep, "no variable on lhs of condition"); + if (condp->lhsp()->castVarRef()->varp() != m_forVarp + || condp->lhsp()->castVarRef()->varScopep() != m_forVscp) + return cantUnroll(nodep, "different variable in condition"); + if (m_generate) V3Const::constifyParam(condp->rhsp()); + AstConst* constStopp = condp->rhsp()->castConst(); + if (!constStopp) return cantUnroll(nodep, "non-constant final value"); + UINFO(8, " Stop expr ok: "<width()>32 || constInitp->num().isFourState() + || constStopp->width()>32 || constStopp->num().isFourState() + || constIncp->width()>32 || constIncp->num().isFourState()) + return cantUnroll(nodep, "init/final/increment too large or four state"); + vlsint32_t valInit = constInitp->num().asInt(); // Extract as unsigned, then make signed + vlsint32_t valStop = constStopp->num().asInt(); // Extract as unsigned, then make signed + if (lte) valStop++; if (gte) valStop--; + vlsint32_t valInc = constIncp->num().asSInt(); + if (subtract) valInc = -valInc; + UINFO(8," In Numbers: for (v="<bodysp(); bodp; bodp=bodp->nextp()) { + bodySize++; + } + if (bodySize > v3Global.opt.unrollStmts()) + return cantUnroll(nodep, "too many statements"); + } + // + // Now, make sure there's no assignment to this variable in the loop + m_varModeCheck = true; + m_varAssignHit = false; + nodep->bodysp()->iterateAndNext(*this); + m_varModeCheck = false; + if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop"); + // + // Finally, we can do it + forUnroller(nodep, constInitp->num(), + condp, constStopp->num(), + incInstrp, constIncp->num()); nodep = NULL; + return true; + } + + void forUnroller(AstNodeFor* nodep, const V3Number& numInit, + AstNodeBiop* cmpInstrp, const V3Number& numStop, + AstNodeBiop* incInstrp, const V3Number& numInc) { + UINFO(4, " Unroll for var="<fileline(), 1); + cmpInstrp->numberOperate(contin, loopValue, numStop); + if (contin.isEqZero()) { + break; // Done with the loop + } else { + AstNode* oneloopp = bodysp->cloneTree(true); + + m_varValuep = new AstConst(nodep->fileline(), loopValue); + m_varModeReplace = true; + oneloopp->iterateAndNext(*this); + m_varModeReplace = false; + + if (newbodysp) newbodysp->addNext(oneloopp); + else newbodysp = oneloopp; + + m_statIters++; + if (++times > v3Global.opt.unrollCount()*3) { + nodep->v3error("Loop unrolling took too long; probably this is an infinite loop."); + break; + } + + //loopValue += valInc + V3Number newnum(nodep->fileline(), m_forVarp->width()); // Can't increment in-place + incInstrp->numberOperate(newnum, loopValue, numInc); + loopValue.opAssign(newnum); + } + } + } + + // And, leave the iterator at the right final value. + if (!nodep->castGenFor()) { + AstVarRef* newrefp = (m_forVscp + ? new AstVarRef(nodep->fileline(), m_forVscp, true) + : new AstVarRef(nodep->fileline(), m_forVarp, true)); + AstAssign* finalAssignp = new AstAssign + (nodep->fileline(), + newrefp, + new AstConst(nodep->fileline(), loopValue)); + if (newbodysp) newbodysp->addNext(finalAssignp); + else newbodysp = finalAssignp; + } + + // Replace the FOR() + if (newbodysp) nodep->replaceWith(newbodysp); + else nodep->unlinkFrBack(); + if (debug()>=9) newbodysp->dumpTree(cout," _new: "); + } + + virtual void visit(AstNodeFor* nodep, AstNUser*) { + if (!m_generate || m_varModeReplace) { + nodep->iterateChildren(*this); + } + if (m_varModeCheck || m_varModeReplace) { + } else { + if (forUnrollCheck(nodep)) { + nodep=NULL; // Did replacement + } else if (m_generate || nodep->castGenFor()) { + nodep->v3error("For loop doesn't have genvar index, or is misformed"); + } else { + // So later optimizations don't need to deal with them, + // convert leftover FOR's: + // FOR(init,cond,assign,body) -> init,WHILE(cond) { body, assign } + AstNode* initsp = nodep->initsp(); if (initsp) initsp->unlinkFrBackWithNext(); + AstNode* condp = nodep->condp(); if (condp) condp->unlinkFrBackWithNext(); + AstNode* assignsp = nodep->assignsp(); if (assignsp) assignsp->unlinkFrBackWithNext(); + AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext(); + bodysp = bodysp->addNext(assignsp); + AstNode* newp = new AstWhile(nodep->fileline(), + condp, + bodysp); + initsp = initsp->addNext(newp); + newp = initsp; + nodep->replaceWith(newp); + nodep->deleteTree(); nodep=NULL; + } + } + } + + virtual void visit(AstBegin* nodep, AstNUser*) { + // Naming inside loop body; must have been a generate for. + // We need to only rename the 'upper' begin, + // anything lower will be renamed "uppernewname.lowerbegin" + bool lastBegin = m_inBegin; + m_inBegin = true; + nodep->iterateChildren(*this); + m_inBegin = lastBegin; + + if (m_varModeReplace && !m_inBegin // above begin, not this one + ) { + // Rename it, as otherwise we may get a conflict + // V3Begin sees these DOTs and makes CellInlines for us. + string nname = (string)"genfor"+cvtToStr(m_varValuep->asInt())+"__DOT__"+nodep->name(); + //UINFO(8," Rename begin "<name(nname); + } + } + + virtual void visit(AstVarRef* nodep, AstNUser*) { + if (m_varModeCheck + && nodep->varp() == m_forVarp + && nodep->varScopep() == m_forVscp + && nodep->lvalue()) { + UINFO(8," Itervar assigned to: "<varp() == m_forVarp + && nodep->varScopep() == m_forVscp) { + AstNode* newconstp = m_varValuep->cloneTree(false); + nodep->replaceWith(newconstp); + } + } + + //-------------------- + // Default: Just iterate + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + UnrollVisitor(AstNode* nodep, bool generate) { + m_forVarp = NULL; + m_forVscp = NULL; + m_varModeCheck = false; + m_varModeReplace = false; + m_inBegin = false; + m_generate = generate; + // + nodep->accept(*this); + } + virtual ~UnrollVisitor() { + V3Stats::addStat("Optimizations, Unrolled Loops", m_statLoops); + V3Stats::addStat("Optimizations, Unrolled Iterations", m_statIters); + } +}; + +//###################################################################### +// Unroll class functions + +void V3Unroll::unrollAll(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include +#include + +#include "V3Global.h" +#include "V3Width.h" +#include "V3Number.h" +#include "V3Const.h" + +//###################################################################### +// Width state, as a visitor of each AstNode + + enum Stage { PRELIM=1,FINAL=2,BOTH=3 }; + + class WidthVP : public AstNUser { + // Parameters to pass down hierarchy with visit functions. + int m_width; // Expression width, for (2+2), it's 32 bits + int m_minWidth; // Minimum width, for (2+2), it's 2 bits, for 32'2+32'2 it's 32 bits + Stage m_stage; // If true, report errors + public: + WidthVP(int width, int minWidth, Stage stage) : m_width(width), m_minWidth(minWidth), m_stage(stage) {}; + int width() const { return m_width; } + int widthMin() const { return m_minWidth?m_minWidth:m_width; } + bool prelim() const { return m_stage&1; } + bool final() const { return m_stage&2; } + }; + + +class WidthVisitor : public AstNVisitor { +private: + // STATE + int m_taskDepth; // Recursion check + bool m_paramsOnly; // Computing parameter value; limit operation + AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations + AstNodeCase* m_casep; // Current case statement CaseItem is under + + //int debug() { return 9; } + + // CLASSES +#define ANYSIZE 0 + + // METHODS + // Naming: width_{output size rule}_{lhs rule}_{rhs rule} + + // VISITORS + // Widths: 1 bit out, lhs 1 bit + void width_O1_L1(AstNode* nodep, AstNUser* vup); + virtual void visit(AstLogNot* nodep, AstNUser* vup) { width_O1_L1(nodep,vup); } + virtual void visit(AstPslBool* nodep, AstNUser* vup) { width_O1_L1(nodep,vup); } + + // Widths: 1 bit out, lhs 1 bit, rhs 1 bit + void width_O1_L1_R1(AstNode* nodep, AstNUser* vup); + virtual void visit(AstLogAnd* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); } + virtual void visit(AstLogOr* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); } + virtual void visit(AstLogIf* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); } + virtual void visit(AstLogIff* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); } + + // Widths: 1 bit out, Any width lhs + void width_O1_L(AstNode* nodep, AstNUser* vup); + virtual void visit(AstRedAnd* nodep, AstNUser* vup) { width_O1_L(nodep,vup); } + virtual void visit(AstRedOr* nodep, AstNUser* vup) { width_O1_L(nodep,vup); } + virtual void visit(AstRedXnor* nodep, AstNUser* vup){ width_O1_L(nodep,vup); } + virtual void visit(AstRedXor* nodep,AstNUser* vup) { width_O1_L(nodep,vup); } + virtual void visit(AstIsUnknown* nodep,AstNUser* vup) { width_O1_L(nodep,vup); } + virtual void visit(AstOneHot* nodep,AstNUser* vup) { width_O1_L(nodep,vup); } + virtual void visit(AstOneHot0* nodep,AstNUser* vup) { width_O1_L(nodep,vup); } + + // Widths: 1 bit out, lhs width == rhs width + void width_O1_L_Rlhs(AstNode* nodep, AstNUser* vup); + virtual void visit(AstEq* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } + virtual void visit(AstEqCase* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } + virtual void visit(AstGt* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } + virtual void visit(AstGtS* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } + virtual void visit(AstGte* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } + virtual void visit(AstGteS* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } + virtual void visit(AstLt* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } + virtual void visit(AstLtS* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } + virtual void visit(AstLte* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } + virtual void visit(AstLteS* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } + virtual void visit(AstNeq* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); } + virtual void visit(AstNeqCase* nodep, AstNUser* vup){ width_O1_L_Rlhs(nodep,vup); } + + // Widths: out width = lhs width = rhs width + void width_Omax_L_Rlhs(AstNode* nodep, AstNUser* vup); + virtual void visit(AstAnd* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + virtual void visit(AstOr* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + virtual void visit(AstXnor* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + virtual void visit(AstXor* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + // Multiple possible reasonable division width conversions. Just keep our code simple, they aren't common. + virtual void visit(AstModDiv* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + virtual void visit(AstModDivS* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + virtual void visit(AstDiv* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + virtual void visit(AstDivS* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + // Special warning suppression rules + virtual void visit(AstSub* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + virtual void visit(AstAdd* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + virtual void visit(AstMul* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + virtual void visit(AstMulS* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + + // Widths: out width = lhs width + void width_Olhs_L(AstNode* nodep, AstNUser* vup); + virtual void visit(AstNot* nodep, AstNUser* vup) { width_Olhs_L(nodep,vup); } + virtual void visit(AstUnaryMin* nodep, AstNUser* vup) { width_Olhs_L(nodep,vup); } + virtual void visit(AstSigned* nodep, AstNUser* vup) { width_Olhs_L(nodep,vup); } + virtual void visit(AstUnsigned* nodep, AstNUser* vup) { width_Olhs_L(nodep,vup); } + + // Widths: Output width from lhs, rhs<33 bits + void width_Olhs_L_R32(AstNode* nodep, AstNUser* vup); + virtual void visit(AstPow* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); } + virtual void visit(AstPowS* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); } + virtual void visit(AstShiftL* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); } + virtual void visit(AstShiftR* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); } + virtual void visit(AstShiftRS* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); } + + // Special cases. So many.... + virtual void visit(AstNodeCond* nodep, AstNUser* vup) { + // op=cond?expr1:expr2 is a Good large example of the propagation mess + if (vup->c()->prelim()) { // First stage evaluation + // Just once, do the conditional, expect one bit out. + nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); + // Determine sub expression widths only relying on what's in the subops + nodep->expr1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->expr2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + // Calculate width of this expression. + // First call (prelim()) vup->c()->width() is probably zero, so we'll return + // the size of this subexpression only. + // Second call (final()) vup->c()->width() is probably the expression size, so + // the expression includes the size of the output too. + int width = max(vup->c()->width(), max(nodep->expr1p()->width(), nodep->expr2p()->width())); + int mwidth = max(vup->c()->widthMin(), max(nodep->expr1p()->widthMin(), nodep->expr2p()->widthMin())); + nodep->width(width,mwidth); + if (vup->c()->final()) { + // Final width known, so make sure children recompute & check their sizes + nodep->expr1p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); + nodep->expr2p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); + // Error report and change sizes for suboperands of this node. + widthCheckReduce(nodep,"Conditional Expression",nodep->condp(),1,0); + widthCheck(nodep,"Conditional True",nodep->expr1p(),width,mwidth); + widthCheck(nodep,"Conditional False",nodep->expr2p(),width,mwidth); + } + } + virtual void visit(AstConcat* nodep, AstNUser* vup) { + if (vup->c()->prelim()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->width(nodep->lhsp()->width() + nodep->rhsp()->width(), + nodep->lhsp()->widthMin() + nodep->rhsp()->widthMin()); + } + if (vup->c()->final()) { + if (!nodep->widthSized()) nodep->v3error("Concat argument must have specific size. (No unsized numbers/parameters)."); + } + } + virtual void visit(AstReplicate* nodep, AstNUser* vup) { + if (vup->c()->prelim()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + V3Const::constifyParam(nodep->rhsp()); + AstConst* constp = nodep->rhsp()->castConst(); + if (!constp) { nodep->v3error("Replication value isn't a constant."); return; } + uint32_t times = constp->asInt(); + if (times==0) { nodep->v3error("Replication value is 0."); times=1; } + nodep->width((nodep->lhsp()->width() * times), + (nodep->lhsp()->widthMin() * times)); + } + if (vup->c()->final()) { + if (!nodep->widthSized()) nodep->v3error("Replication expression must have specific size."); + } + } + virtual void visit(AstRange* nodep, AstNUser* vup) { + if (vup->c()->prelim()) { + nodep->msbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->lsbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + V3Const::constifyParam(nodep->msbp()); + V3Const::constifyParam(nodep->lsbp()); + AstConst* msbConstp = nodep->msbp()->castConst(); + AstConst* lsbConstp = nodep->lsbp()->castConst(); + if (!msbConstp || !lsbConstp) { + if (!msbConstp) nodep->v3error("MSB of bit range isn't a constant"); + if (!lsbConstp) nodep->v3error("LSB of bit range isn't a constant"); + nodep->width(1,1); return; + } + uint32_t msb = msbConstp->asInt(); + uint32_t lsb = lsbConstp->asInt(); + if (msb > (1UL<<28)) nodep->v3error("MSB of bit range is huge; vector of over 1billion bits: 0x"<backp()->castRange()) huntbackp=huntbackp->backp(); + if (huntbackp->backp()->castVar() + && huntbackp->backp()->castVar()->arraysp()==huntbackp) { + AstNRelinker msbHandle; + AstNRelinker lsbHandle; + msbConstp->unlinkFrBack(&msbHandle); + lsbConstp->unlinkFrBack(&lsbHandle); + msbHandle.relink(lsbConstp); + lsbHandle.relink(msbConstp); + } else { + nodep->v3error("Unsupported: MSB < LSB of bit range: "<width(width,width); + } + } + virtual void visit(AstSel* nodep, AstNUser* vup) { + if (vup->c()->prelim()) { + nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->lsbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->widthp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + V3Const::constifyParam(nodep->widthp()); + AstConst* widthConstp = nodep->widthp()->castConst(); + if (!widthConstp) { + nodep->v3error("Width of bit extract isn't a constant"); + nodep->width(1,1); return; + } + int width = nodep->widthConst(); + nodep->width(width,width); + if (nodep->lsbp()->castConst() + && nodep->msbConst() < nodep->lsbConst()) { + nodep->v3error("Unsupported: MSB < LSB of bit extract: " + <msbConst()<<"<"<lsbConst()); + nodep->lsbp()->replaceWith(new AstConst(nodep->lsbp()->fileline(), 0)); + } + // We're extracting, so just make sure the expression is at least wide enough. + if (nodep->fromp()->width() < width) { + nodep->v3error("Extracting "<fromp()->width()<<" bit number"); + // Extend it. + widthCheck(nodep,"errorless...",nodep->fromp(),width,width,true/*noerror*/); + } + // Check bit indexes. + // What is the MSB? We want the true MSB, not one starting at 0, + // because a 4 bit index is required to look at a one-bit variable[15:15] + int frommsb = nodep->fromp()->width() - 1; + int fromlsb = 0; + AstNodeVarRef* varrp = nodep->fromp()->castNodeVarRef(); + if (varrp && varrp->varp()->rangep()) { // Selecting a bit from a multibit register + frommsb = varrp->varp()->msb(); + fromlsb = varrp->varp()->lsb(); + } + int selwidth = V3Number::log2b(frommsb+1-1)+1; // Width to address a bit + nodep->fromp()->iterateAndNext(*this,WidthVP(selwidth,selwidth,BOTH).p()); + if (widthBad(nodep->lsbp(),selwidth,selwidth) + && nodep->lsbp()->width()!=32) + nodep->v3warn(WIDTH,"Bit extraction of var["<lsbp()->width() + <<(nodep->lsbp()->width()!=nodep->lsbp()->widthMin() + ?" or "+cvtToStr(nodep->lsbp()->widthMin()):"") + <<" bits."); + widthCheck(nodep,"Extract Range",nodep->lsbp(),selwidth,selwidth,true); + } + } + + virtual void visit(AstArraySel* nodep, AstNUser* vup) { + if (vup->c()->prelim()) { + nodep->bitp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + AstNode* basefromp = AstArraySel::baseFromp(nodep->fromp()); + int dimension = AstArraySel::dimension(nodep->fromp()); + AstNodeVarRef* varrp = basefromp->castNodeVarRef(); + if (!varrp) nodep->v3fatalSrc("No VarRef found under ArraySel(s)"); + // + int frommsb; + int fromlsb; + if (!varrp->varp()->arrayp(dimension)) { + nodep->v3fatalSrc("Array reference exceeds dimension of array"); + } + if (1) { // ARRAY slice extraction + int outwidth = varrp->width(); // Width of variable + frommsb = varrp->varp()->arrayp(dimension)->msbConst(); + fromlsb = varrp->varp()->arrayp(dimension)->lsbConst(); + if (fromlsb>frommsb) {int t=frommsb; frommsb=fromlsb; fromlsb=t; } + nodep->width(outwidth,outwidth); // Width out = width of array + } + int selwidth = V3Number::log2b(frommsb+1-1)+1; // Width to address a bit + nodep->fromp()->iterateAndNext(*this,WidthVP(selwidth,selwidth,BOTH).p()); + if (widthBad(nodep->bitp(),selwidth,selwidth) + && nodep->bitp()->width()!=32) + nodep->v3warn(WIDTH,"Bit extraction of array["<bitp()->width() + <<(nodep->bitp()->width()!=nodep->bitp()->widthMin() + ?" or "+cvtToStr(nodep->bitp()->widthMin()):"") + <<" bits."); + widthCheck(nodep,"Extract Range",nodep->bitp(),selwidth,selwidth,true); + } + } + virtual void visit(AstExtend* nodep, AstNUser* vup) { + // Only created by this process, so we know width from here down it is correct. + } + virtual void visit(AstExtendS* nodep, AstNUser* vup) { + // Only created by signing process, so we know width from here down it is correct. + } + virtual void visit(AstConst* nodep, AstNUser* vup) { + if (vup && vup->c()->prelim()) { + if (nodep->num().sized()) { + nodep->width(nodep->num().width(), nodep->num().width()); + } else { + nodep->width(nodep->num().width(), nodep->num().minWidth()); + } + } + } + virtual void visit(AstTime* nodep, AstNUser*) { + nodep->width(64,64); + } + virtual void visit(AstUCFunc* nodep, AstNUser* vup) { + // Give it the size the user wants. + if (vup && vup->c()->prelim()) { + nodep->width(32,1); // We don't care + } + if (vup->c()->final()) { + nodep->width(vup->c()->width(),vup->c()->widthMin()); // We don't care + if (nodep->width()>64) nodep->v3error("Unsupported: $c can't generate wider then 64 bits"); + } + // Just let all arguments seek their natural sizes + nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + virtual void visit(AstCountOnes* nodep, AstNUser* vup) { + if (vup->c()->prelim()) { + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + // If it's a 32 bit number, we need a 6 bit number as we need to return '32'. + int selwidth = V3Number::log2b(nodep->lhsp()->width())+1; + nodep->width(selwidth,selwidth); + } + } + virtual void visit(AstAttrOf* nodep, AstNUser*) { + nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->width(32,1); // Approximation, unsized 32 + } + virtual void visit(AstText* nodep, AstNUser* vup) { + // Only used in CStmts which don't care.... + } + virtual void visit(AstVar* nodep, AstNUser* vup) { + //if (debug()) nodep->dumpTree(cout," InitPre: "); + // Must have deterministic constant width + int width=1; int mwidth=1; + nodep->arraysp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + if (nodep->rangep()) { + nodep->rangep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + width = mwidth = nodep->rangep()->width(); + } + else if (nodep->isInteger() || nodep->isGenVar()) { + width = 32; + mwidth = 1; // Consider it unsized, as we want x[int] to work, and also want {int} to fail. + } + else if (nodep->isParam()) { // unranged + width = mwidth = 0; // But see below later. + } + if (nodep->initp()) { + nodep->initp()->iterateAndNext(*this,WidthVP(width,0,BOTH).p()); + if (nodep->isParam() && !nodep->rangep()) { + if (nodep->initp()->widthSized()) { + width = mwidth = nodep->initp()->width(); + } else { + if (nodep->initp()->width()>32) nodep->initp()->v3warn(WIDTH,"Assigning >32 bit to unranged parameter (defaults to 32 bits)\n"); + width = 32; + mwidth = nodep->initp()->widthMin(); + } + } + //if (debug()) nodep->dumpTree(cout," final: "); + } + if (nodep->isParam() && !nodep->rangep()) { + // Parameter sizes can come from the thing they get assigned from + // They then "stick" to that width. + if (!width) width=32; // Or, if nothing, they're 32 bits. + nodep->rangep(new AstRange(nodep->fileline(),width-1,0)); + nodep->rangep()->width(width,width); + } + nodep->width(width,mwidth); // No need to check; varrefs are the "checkers" + if (nodep->initp()) widthCheck(nodep,"Initial value",nodep->initp(),width,mwidth); + UINFO(4,"varWidthed "<dumpTree(cout," InitPos: "); + } + virtual void visit(AstNodeVarRef* nodep, AstNUser* vup) { + if (nodep->varp()->width()==0) { + // Var hasn't been widthed, so make it so. + nodep->varp()->iterate(*this); + } + if ((nodep->varp()->isInteger() || nodep->varp()->isGenVar()) + && nodep->backp()->castNodeAssign()) { // On LHS + // Consider Integers on LHS to sized (else would be unsized.) + nodep->width(32,32); + } else { + nodep->width(nodep->varp()->width(), nodep->varp()->widthMin()); + } + } + virtual void visit(AstPslClocked* nodep, AstNUser*) { + nodep->propp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); + nodep->sensesp()->iterate(*this); + widthCheckReduce(nodep,"Property",nodep->propp(),1,1); // it's like a if() condition. + nodep->width(1,1); + } + + //-------------------- + // Top levels + + virtual void visit(AstNodeCase* nodep, AstNUser*) { + // TOP LEVEL NODE + AstNodeCase* lastCasep = m_casep; + m_casep = nodep; + nodep->exprp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) { + itemp->iterate(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + int width = nodep->exprp()->width(); + int mwidth = nodep->exprp()->widthMin(); + for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) { + width = max(width,itemp->width()); + mwidth = max(mwidth,itemp->widthMin()); + } + nodep->exprp()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); + for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) { + itemp->iterate(*this,WidthVP(width,mwidth,FINAL).p()); + } + widthCheck(nodep,"Case expression",nodep->exprp(),width,mwidth); + m_casep = lastCasep; + } + virtual void visit(AstCaseItem* nodep, AstNUser* vup) { + // Same for both prelim() and final() + if (!m_casep->castGenCase()) nodep->bodysp()->iterateAndNext(*this); + if (!nodep->condsp()) { + // Else "default:" of the case, just return benign value + nodep->width(vup->c()->width(),vup->c()->widthMin()); + } else { + // Need to look across multiple case values for one set of statements + int width = nodep->condsp()->width(); + int mwidth = nodep->condsp()->widthMin(); + for (AstNode* condp = nodep->condsp(); condp; condp=condp->nextp()) { + condp->iterate(*this,vup); + width = max(width,condp->width()); + mwidth = max(mwidth,condp->widthMin()); + if (vup->c()->final()) { + widthCheck(nodep,"Case Item",condp,vup->c()->width(),vup->c()->widthMin()); + } + } + nodep->width(width,mwidth); + } + } + virtual void visit(AstNodeFor* nodep, AstNUser*) { + // TOP LEVEL NODE + nodep->initsp()->iterateAndNext(*this); + nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); + if (!nodep->castGenFor()) nodep->bodysp()->iterateAndNext(*this); + nodep->assignsp()->iterateAndNext(*this); + widthCheckReduce(nodep,"For Test Condition",nodep->condp(),1,1); // it's like a if() condition. + } + virtual void visit(AstWhile* nodep, AstNUser*) { + // TOP LEVEL NODE + nodep->precondsp()->iterateAndNext(*this); + nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); + nodep->bodysp()->iterateAndNext(*this); + widthCheckReduce(nodep,"For Test Condition",nodep->condp(),1,1); // it's like a if() condition. + } + virtual void visit(AstNodeIf* nodep, AstNUser*) { + // TOP LEVEL NODE + //if (debug()) nodep->dumpTree(cout," IfPre: "); + if (!nodep->castGenIf()) { + nodep->ifsp()->iterateAndNext(*this); + nodep->elsesp()->iterateAndNext(*this); + } + nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); + widthCheckReduce(nodep,"If",nodep->condp(),1,1); // it's like a if() condition. + //if (debug()) nodep->dumpTree(cout," IfPos: "); + } + virtual void visit(AstNodeAssign* nodep, AstNUser*) { + // TOP LEVEL NODE + //if (debug()) nodep->dumpTree(cout," AssignPre: "); + nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + if(!nodep->lhsp()->widthSized()) nodep->v3fatalSrc("How can LHS be unsized?"); + int awidth = nodep->lhsp()->width(); + if (awidth==0) { + awidth = nodep->rhsp()->width(); // Parameters can propagate by unsized assignment + } + nodep->rhsp()->iterateAndNext(*this,WidthVP(awidth,awidth,FINAL).p()); + nodep->width(awidth,awidth); // We know the assign will truncate, so rather + //UINFO(0,"aw "<rhsp()->width()<<" m"<rhsp()->widthMin()<rhsp(),awidth,awidth); + //if (debug()) nodep->dumpTree(cout," AssignPos: "); + } + virtual void visit(AstNodePli* nodep, AstNUser*) { + // Excludes NodeDisplay, see below + // TOP LEVEL NODE + // Just let all arguments seek their natural sizes + nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + virtual void visit(AstDisplay* nodep, AstNUser*) { + if (nodep->filep()) nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + // Just let all arguments seek their natural sizes + nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + virtual void visit(AstFOpen* nodep, AstNUser*) { + nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + nodep->filenamep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + nodep->modep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + virtual void visit(AstFClose* nodep, AstNUser*) { + nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p()); + } + virtual void visit(AstUCStmt* nodep, AstNUser*) { + // TOP LEVEL NODE + // Just let all arguments seek their natural sizes + nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + virtual void visit(AstPslCover* nodep, AstNUser*) { + // TOP LEVEL NODE + nodep->propp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); + widthCheckReduce(nodep,"Property",nodep->propp(),1,1); // it's like a if() condition. + } + virtual void visit(AstPslAssert* nodep, AstNUser*) { + // TOP LEVEL NODE + nodep->propp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p()); + widthCheckReduce(nodep,"Property",nodep->propp(),1,1); // it's like a if() condition. + } + virtual void visit(AstPin* nodep, AstNUser*) { + //if (debug()) nodep->dumpTree(cout,"- PinPre: "); + // TOP LEVEL NODE + if (nodep->modVarp() && nodep->modVarp()->isGParam()) { + // Widthing handled as special init() case + nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } else if (!m_paramsOnly){ + if (nodep->modVarp()->width()==0) { + // Var hasn't been widthed, so make it so. + nodep->modVarp()->iterate(*this); + } + // Very much like like an assignment, but which side is LH/RHS + // depends on pin being a in/output/inout. + nodep->exprp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + int pinwidth = nodep->modVarp()->width(); + int expwidth = nodep->exprp()->width(); + bool inputPin = nodep->modVarp()->isInput(); + int awidth; + if (m_cellRangep) { + int numInsts = m_cellRangep->width(); + if (expwidth == pinwidth) { + awidth = pinwidth; // Arrayed instants: widths match so connect to each instance + } else if (expwidth == pinwidth*numInsts) { + awidth = pinwidth; // Arrayed instants: one bit for each of the instants (each assign is 1 pinwidth wide) + } else { + // Must be a error according to spec + // (Because we need to know if to connect to one or all instants) + nodep->v3error("Port connection "<prettyName()<<" as part of a module instance array " + <<" requires "<exprp()->typeName() + <<" generates "<width(awidth,awidth); + } else { + if (nodep->modVarp()->isTristate()) nodep->v3error("Unsupported: inouts: Verilator is a 2 state simulator\n"); + // if support tristates need some mess to force both sides to proper size + if (inputPin) { + // input pin is lhs, expr is rhs; resize expr to match + awidth = pinwidth; + } else { + // output pin is rhs, expr is lhs + // We can't make the RANGE/EXTEND until V3Inst phase, as need RHS of assignment + awidth = expwidth; + } + nodep->width(awidth,awidth); + widthCheckPin(nodep, nodep->exprp(), pinwidth, inputPin); + } + nodep->exprp()->iterateAndNext(*this,WidthVP(awidth,awidth,FINAL).p()); + } + //if (debug()) nodep->dumpTree(cout,"- PinPos: "); + } + virtual void visit(AstCell* nodep, AstNUser*) { + if (!m_paramsOnly) { + if (nodep->rangep()) { + m_cellRangep = nodep->rangep(); + nodep->rangep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + nodep->pinsp()->iterateAndNext(*this); + } + nodep->paramsp()->iterateAndNext(*this); + m_cellRangep = NULL; + } + virtual void visit(AstFunc* nodep, AstNUser* vup) { + // Grab width from the output variable + UINFO(5," FUNC "<width()==0) { + // Function hasn't been widthed, so make it so. + nodep->iterateChildren(*this); + nodep->width(nodep->fvarp()->width(), nodep->fvarp()->width()); + } + } + virtual void visit(AstFuncRef* nodep, AstNUser* vup) { + visit(nodep->castNodeFTaskRef(), vup); + nodep->widthSignedFrom(nodep->taskp()); + } + virtual void visit(AstNodeFTaskRef* nodep, AstNUser* vup) { + // Function hasn't been widthed, so make it so. + if (nodep->taskp()->width()==0) { + if (m_taskDepth > 100) { + nodep->v3error("Unsupported: Recursive function or task call\n"); + nodep->width(1,1); + nodep->taskp()->width(1,1); + return; + } + m_taskDepth++; + nodep->taskp()->iterate(*this); + m_taskDepth--; + } + // And do the arguments to the task/function too + AstNode* pinp = nodep->pinsp(); + AstNode* stmt_nextp; // List may change, so need to keep pointer + for (AstNode* stmtp = nodep->taskp()->stmtsp(); stmtp; stmtp=stmt_nextp) { + stmt_nextp = stmtp->nextp(); + if (AstVar* portp = stmtp->castVar()) { + if (portp->isIO() + && pinp!=NULL) { // Else argument error we'll find later + AstNode* pin_nextp = pinp->nextp(); // List may change, so remember nextp + int width = portp->width(); + int ewidth = portp->widthMin(); + pinp->accept(*this,WidthVP(width,ewidth,BOTH).p()); + widthCheck(nodep,"Function Argument",pinp,width,ewidth); + pinp = pin_nextp; + } + } + } + } + virtual void visit(AstNetlist* nodep, AstNUser*) { + // Iterate modules backwards, in bottom-up order. That's faster + nodep->iterateChildrenBackwards(*this); + } + + //-------------------- + // Default + virtual void visit(AstNode* nodep, AstNUser* vup) { + // Default: Just iterate + if (vup) nodep->v3fatalSrc("Visit function missing? Widthed expectation for this node: "<iterateChildren(*this); + } + + // METHODS + bool widthBad (AstNode* nodep, int expWidth, int expWidthMin); + void widthCheck (AstNode* nodep, const char* side, + AstNode* underp, int expWidth, int expWidthMin, + bool ignoreWarn=false); + void widthCheckReduce (AstNode* nodep, const char* side, + AstNode* underp, int expWidth, int expWidthMin, + bool ignoreWarn=false); + void widthCheckPin (AstNode* nodep, AstNode* underp, int expWidth, bool inputPin); + bool fixAutoExtend (AstNode*& nodepr, int expWidth); + void fixWidthExtend (AstNode* nodep, int expWidth); + void fixWidthReduce (AstNode* nodep, int expWidth); + +public: + // CONSTUCTORS + WidthVisitor(AstNode* nodep, bool paramsOnly) { + m_paramsOnly = paramsOnly; + m_taskDepth = 0; + m_cellRangep = NULL; + m_casep = NULL; + nodep->accept(*this); + } + virtual ~WidthVisitor() {} +}; + +//---------------------------------------------------------------------- +// METHODs + +bool WidthVisitor::widthBad (AstNode* nodep, int expWidth, int expWidthMin) { + if (nodep->width()==0) nodep->v3fatalSrc("Under node has no expected width?? Missing Visitor func?"); + if (expWidth==0) nodep->v3fatalSrc("Node has no expected width?? Missing Visitor func?"); + if (expWidthMin==0) expWidthMin = expWidth; + if (nodep->widthSized() && nodep->width() != expWidthMin) return true; + if (!nodep->widthSized() && nodep->widthMin() > expWidthMin) return true; + return false; +} + +void WidthVisitor::fixWidthExtend (AstNode* nodep, int expWidth) { + // Fix the width mismatch by extending or truncating bits + // Truncation is rarer, but can occur: parameter [3:0] FOO = 64'h12312; + // A(CONSTwide)+B becomes A(CONSTwidened)+B + // A(somewide)+B becomes A(TRUNC(somewide,width))+B + // or A(EXTRACT(somewide,width,0))+B + UINFO(4," widthExtend_old: "<castConst(); + if (constp && !nodep->isSigned()) { + // Save later constant propagation work, just right-size it. + V3Number num (nodep->fileline(), expWidth); + num.opAssign(constp->num()); + AstNode* newp = new AstConst(nodep->fileline(), num); + newp->signedFrom(constp); + constp->replaceWith(newp); + nodep=newp; + } else if (expWidthwidth()) { + // Trunc - Extract + AstNRelinker linker; + nodep->unlinkFrBack(&linker); + AstNode* newp = new AstSel(nodep->fileline(), nodep, 0, expWidth); + linker.relink(newp); + nodep=newp; + } else { + // Extend + AstNRelinker linker; + nodep->unlinkFrBack(&linker); + AstNode* newp = (nodep->isSigned() + ? (new AstExtendS(nodep->fileline(), nodep))->castNode() + : (new AstExtend (nodep->fileline(), nodep))->castNode()); + linker.relink(newp); + nodep=newp; + } + nodep->width(expWidth,expWidth); + UINFO(4," _new: "<castConst(); + if (constp) { + V3Number num (nodep->fileline(), expWidth); + num.opRedOr(constp->num()); + AstNode* newp = new AstConst(nodep->fileline(), num); + newp->signedFrom(constp); + constp->replaceWith(newp); + nodep=newp; + } else { + AstNRelinker linker; + nodep->unlinkFrBack(&linker); + AstNode* newp = new AstRedOr(nodep->fileline(), nodep); + linker.relink(newp); + nodep=newp; + } + nodep->width(expWidth,expWidth); + UINFO(4," _new: "<castConst()) { + if (constp->num().autoExtend() && !constp->num().sized() && constp->width()==1) { + // Make it the proper size. Careful of proper extension of 0's/1's + V3Number num (constp->fileline(), expWidth); + num.opRepl(constp->num(), expWidth); // {width{'1}} + AstNode* newp = new AstConst(constp->fileline(), num); + // Spec says always unsigned with proper width + if (debug()>4) constp->dumpTree(cout," fixAutoExtend_old: "); + if (debug()>4) newp->dumpTree(cout," _new: "); + constp->replaceWith(newp); + constp->deleteTree(); constp=NULL; + // Tell caller the new constp, and that we changed it. + nodepr = newp; + return true; + } + } + return false; // No change +} + +void WidthVisitor::widthCheck (AstNode* nodep, const char* side, + AstNode* underp, int expWidth, int expWidthMin, + bool ignoreWarn) { + //UINFO(0,"wchk "<4) nodep->backp()->dumpTree(cout," back: "); + nodep->v3warn(WIDTH,"Operator "<typeName() + <<" expects "<typeName()<<" generates "<width() + <<(underp->width()!=underp->widthMin() + ?" or "+cvtToStr(underp->widthMin()):"") + <<" bits."); + } + if (bad || underp->width()!=expWidth) { + fixWidthExtend(underp, expWidth); underp=NULL;//Changed + } +} + +void WidthVisitor::widthCheckReduce (AstNode* nodep, const char* side, + AstNode* underp, int expWidth, int expWidthMin, + bool ignoreWarn) { + if (expWidthMin==0) expWidthMin = expWidth; + if (expWidth!=1) nodep->v3fatalSrc("Only for binary functions"); + bool bad = widthBad(underp,expWidth,expWidthMin); + if (bad) { + if (!ignoreWarn) { + if (debug()>4) nodep->backp()->dumpTree(cout," back: "); + nodep->v3warn(WIDTH,"Logical Operator "<typeName() + <<" expects 1 bit on the "<typeName()<<" generates "<width() + <<(underp->width()!=underp->widthMin() + ?" or "+cvtToStr(underp->widthMin()):"") + <<" bits."); + } + fixWidthReduce(underp, expWidth); underp=NULL;//Changed + } +} + +void WidthVisitor::widthCheckPin (AstNode* nodep, AstNode* underp, int expWidth, bool inputPin) { + bool bad = widthBad(underp,expWidth,expWidth); + if (bad && fixAutoExtend(underp/*ref*/,expWidth)) bad=false; // Changes underp + if (bad) { + nodep->v3warn(WIDTH,(inputPin?"Input":"Output") + <<" port connection "<prettyName() + <<" expects "<typeName()<<" generates "<width() + <<(underp->width()!=underp->widthMin() + ?" or "+cvtToStr(underp->widthMin()):"") + <<" bits."); + } + // We only fix input mismatches + if (bad && inputPin) { + fixWidthExtend(underp, expWidth); underp=NULL;//Changed + } +} + +void WidthVisitor::width_O1_L1(AstNode* nodep, AstNUser* vup) { + // Widths: 1 bit out, lhs 1 bit + // We calculate the width of the UNDER expression. + // We then check its width to see if it's legal, and edit if not + // We finally set the width of our output + if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p()); + } + nodep->width(1,1); + if (vup->c()->final()) { + widthCheckReduce(nodep,"LHS",nodep->op1p(),1,1); + } +} + +void WidthVisitor::width_O1_L1_R1(AstNode* nodep, AstNUser* vup) { + // Widths: 1 bit out, lhs 1 bit, rhs 1 bit + if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p()); + } + nodep->width(1,1); + if (vup->c()->final()) { + widthCheckReduce(nodep,"LHS",nodep->op1p(),1,1); + widthCheckReduce(nodep,"RHS",nodep->op2p(),1,1); + } +} + +void WidthVisitor::width_O1_L(AstNode* nodep, AstNUser* vup) { + // Widths: 1 bit out, Any width lhs + if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + nodep->width(1,1); +} + +void WidthVisitor::width_O1_L_Rlhs(AstNode* nodep, AstNUser* vup) { + // Widths: 1 bit out, lhs width == rhs width + if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + int width = max(nodep->op1p()->width(), nodep->op2p()->width()); + int ewidth = max(nodep->op1p()->widthMin(), nodep->op2p()->widthMin()); + nodep->width(1,1); + if (vup->c()->final()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + widthCheck(nodep,"LHS",nodep->op1p(),width,ewidth); + widthCheck(nodep,"RHS",nodep->op2p(),width,ewidth); + } +} + +void WidthVisitor::width_Olhs_L(AstNode* nodep, AstNUser* vup) { + // Widths: out width = lhs width + // "Interim results shall take the max of operands, including LHS of assignments" + if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + int width = max(vup->c()->width(), nodep->op1p()->width()); + int ewidth = max(vup->c()->widthMin(), nodep->op1p()->widthMin()); + nodep->width(width,ewidth); + if (vup->c()->final()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + widthCheck(nodep,"LHS",nodep->op1p(),width,ewidth); + } +} + +void WidthVisitor::width_Olhs_L_R32(AstNode* nodep, AstNUser* vup) { + // Widths: Output width from lhs, rhs<33 bits + if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); + if (vup->c()->prelim()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); + } + int width = max(vup->c()->width(), nodep->op1p()->width()); + int ewidth = max(vup->c()->widthMin(), nodep->op1p()->widthMin()); + nodep->width(width,ewidth); + if (vup->c()->final()) { + nodep->op1p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p()); + widthCheck(nodep,"LHS",nodep->op1p(),width,ewidth); + if (nodep->op2p()->width()>32) + nodep->op2p()->v3error("Unsupported: Shifting of by a over 32 bit number isn't supported." + <<" (This isn't a shift of 32 bits, but a shift of 2^32, or 4 billion!)\n"); + } +} + +void WidthVisitor::width_Omax_L_Rlhs(AstNode* nodep, AstNUser* vup) { + // Widths: out width = lhs width = rhs width + if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!"); + // If errors are off, we need to follow the spec; thus we really need to do the max() + // because the rhs could be larger, and we need to have proper editing to get the widths + // to be the same for our operations. + if (vup->c()->prelim()) { // First stage evaluation + // Determine expression widths only relying on what's in the subops + nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p()); + } + int width = max(vup->c()->width(), max(nodep->op1p()->width(), nodep->op2p()->width())); + int mwidth = max(vup->c()->widthMin(), max(nodep->op1p()->widthMin(), nodep->op2p()->widthMin())); + nodep->width(width,mwidth); + if (vup->c()->final()) { + // Final call, so make sure children check their sizes + nodep->op1p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); + nodep->op2p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p()); + // Some warning suppressions + bool lhsOk=false; bool rhsOk = false; + if (nodep->castAdd() || nodep->castSub()) { + lhsOk = (mwidth == (nodep->op1p()->widthMin()+1)); // Ok if user wants extra bit from carry + rhsOk = (mwidth == (nodep->op2p()->widthMin()+1)); // Ok if user wants extra bit from carry + } else if (nodep->castMul() || nodep->castMulS()) { + lhsOk = (mwidth >= (nodep->op1p()->widthMin())); + rhsOk = (mwidth >= (nodep->op2p()->widthMin())); + } + // Error report and change sizes for suboperands of this node. + widthCheck(nodep,"LHS",nodep->op1p(),width,mwidth,lhsOk); + widthCheck(nodep,"RHS",nodep->op2p(),width,mwidth,rhsOk); + } +} + +//###################################################################### + +class WidthCommitVisitor : public AstNVisitor { + // Now that all widthing is complete, + // Copy all width() to widthMin(). V3Const expects this +private: + // VISITORS + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->width(nodep->width(),nodep->width()); + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + WidthCommitVisitor(AstNode* nodep) { + nodep->iterateAndNext(*this, NULL); + } + virtual ~WidthCommitVisitor() {} +}; + +//###################################################################### +// Width class functions + +void V3Width::width(AstNetlist* nodep) { + UINFO(2,__FUNCTION__<<": "< +#include + +#include "V3Active.h" +#include "V3ActiveTop.h" +#include "V3Assert.h" +#include "V3AssertPre.h" +#include "V3Begin.h" +#include "V3Branch.h" +#include "V3Case.h" +#include "V3Cast.h" +#include "V3Changed.h" +#include "V3Clean.h" +#include "V3Clock.h" +#include "V3Combine.h" +#include "V3Const.h" +#include "V3Coverage.h" +#include "V3Dead.h" +#include "V3Delayed.h" +#include "V3Depth.h" +#include "V3Descope.h" +#include "V3EmitC.h" +#include "V3EmitMk.h" +#include "V3EmitV.h" +#include "V3Expand.h" +#include "V3File.h" +#include "V3Gate.h" +#include "V3Graph.h" +#include "V3GenClk.h" +#include "V3Inline.h" +#include "V3Inst.h" +#include "V3Life.h" +#include "V3LifePost.h" +#include "V3Link.h" +#include "V3LinkCells.h" +#include "V3LinkDot.h" +#include "V3LinkLevel.h" +#include "V3LinkResolve.h" +#include "V3Localize.h" +#include "V3Name.h" +#include "V3Order.h" +#include "V3Param.h" +#include "V3PreShell.h" +#include "V3Premit.h" +#include "V3Read.h" +#include "V3Scope.h" +#include "V3Signed.h" +#include "V3Split.h" +#include "V3Stats.h" +#include "V3Subst.h" +#include "V3Table.h" +#include "V3Task.h" +#include "V3Trace.h" +#include "V3TraceDecl.h" +#include "V3Unknown.h" +#include "V3Unroll.h" +#include "V3Width.h" + +V3Global v3Global; + +//###################################################################### +// V3 Class -- top level + +void V3Global::readFiles() { + V3Read reader (m_rootp); + // Read libraries + for (V3StringSet::iterator it = v3Global.opt.libraryFiles().begin(); + it != v3Global.opt.libraryFiles().end(); ++it) { + string filename = *it; + reader.readFile(new FileLine("CommandLine",0), filename, true); + } + // Read top module + reader.readFile(new FileLine("CommandLine",0), opt.top(), false); + V3Error::abortIfErrors(); +} + +//###################################################################### + +void process () { + // Resolve all modules cells refer to + V3LinkCells::link(v3Global.rootp()); + V3LinkLevel::modSortByLevel(); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("cells.tree")); + V3Error::abortIfErrors(); + + // Cross-link signal names + V3Link::link(v3Global.rootp()); + // Cross-link dotted hierarchical references + V3LinkDot::linkDot(v3Global.rootp()); + // Correct state we couldn't know at parse time, repair SEL's, set lvalue's + V3LinkResolve::linkResolve(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("link.tree")); + V3Error::abortIfErrors(); + + if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "Link"); + + // Remove parameters by cloning modules to de-parameterized versions + // This requires some width calculations and constant propagation + V3Param::param(v3Global.rootp()); + V3LinkDot::linkDot(v3Global.rootp()); // Cleanup as made new modules + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param.tree")); + V3Error::abortIfErrors(); + + // Remove any modules that were parameterized and are no longer referenced. + V3Dead::deadifyAll(v3Global.rootp(), false); + //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("dead.tree")); + v3Global.checkTree(); + + // Calculate and check widths, edit tree to TRUNC/EXTRACT any width mismatches + V3Width::width(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("width.tree")); + + // Compute signed/unsigned + V3Signed::signedAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("signed.tree")); + V3Error::abortIfErrors(); + + // Commit to the widths we've chosen; Make widthMin==width + V3Width::widthCommit(v3Global.rootp()); + v3Global.assertWidthsSame(true); + //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("widthcommit.tree")); + + // Coverage insertion + // Before we do dead code elimination and inlining, or we'll loose it. + if (v3Global.opt.coverage()) { + V3Coverage::coverage(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("coverage.tree")); + } + + // Assertion insertion + // After we've added block coverage, but before other nasty transforms + if (v3Global.opt.assertOn() || v3Global.opt.psl()) { + V3AssertPre::assertPreAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("assertpre.tree")); + // + V3Assert::assertAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("assert.tree")); + } + + // Add top level wrapper with instance pointing to old top + // Must do this after we know the width of any parameters + // We also do it after coverage/assertion insertion so we don't 'cover' the top level. + V3LinkLevel::wrapTop(v3Global.rootp()); + + // Propagate constants into expressions + V3Const::constifyAllLint(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); + + // Remove cell arrays (must be between V3Width and tasking) + V3Inst::dearrayAll(v3Global.rootp()); + //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("dearray.tree")); + + // Move assignments from X into MODULE temps. + // (Before flattening, so each new X variable is shared between all scopes of that module.) + V3Unknown::unknownAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("unknown.tree")); + + // Task inlining & pushing BEGINs names to variables/cells + // Begin processing must be after Param, before module inlining + V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("begin.tree")); + + // Module inlining + // Cannot remove dead variables after this, as alias information for final + // V3Scope's V3LinkDot is in the AstVar. + if (v3Global.opt.oInline()) { + V3Inline::inlineAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("inline.tree")); + V3LinkDot::linkDot(v3Global.rootp()); // Cleanup as made new modules + //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot.tree")); + } + + //--PRE-FLAT OPTIMIZATIONS------------------ + + // Initial const/dead to reduce work for ordering code + V3Const::constifyAll(v3Global.rootp()); + v3Global.checkTree(); + V3Dead::deadifyAll(v3Global.rootp(), false); + v3Global.checkTree(); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); + + V3Error::abortIfErrors(); + + //--FLATTENING--------------- + + // We're going to flatten the hierarchy, so as many optimizations that + // can be done as possible should be before this.... + + // Convert instantiations to wassigns and always blocks + V3Inst::instAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("inst.tree")); + + // Inst may have made lots of concats; fix them + V3Const::constifyAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); + + // Flatten hierarchy, creating a SCOPE for each module's usage as a cell + V3Scope::scopeAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("scope.tree")); + V3LinkDot::linkDotScope(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot.tree")); + + //--SCOPE BASED OPTIMIZATIONS-------------- + + // Cleanup + V3Const::constifyAll(v3Global.rootp()); + V3Dead::deadifyAll(v3Global.rootp(), false); + v3Global.checkTree(); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); + + // Inline all tasks + V3Task::taskAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("task.tree")); + + // Add __PVT's + V3Name::nameAll(v3Global.rootp()); + //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("name.tree")); + + // Loop unrolling & convert FORs to WHILEs + V3Unroll::unrollAll(v3Global.rootp()); + //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("unroll.tree")); + + // Convert case statements to if() blocks. Must be after V3Unknown + V3Case::caseAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("case.tree")); + + V3Const::constifyAll(v3Global.rootp()); + //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); + + // Make large low-fanin logic blocks into lookup tables + // This should probably be done much later, once we have common logic elimination. + if (v3Global.opt.oTable()) { + V3Table::tableAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("table.tree")); + } + + // Cleanup + V3Const::constifyAll(v3Global.rootp()); + V3Dead::deadifyAll(v3Global.rootp(), false); + v3Global.checkTree(); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); + + // Move assignments/sensitives into a SBLOCK for each unique sensitivity list + // (May convert some ALWAYS to combo blocks, so should be before V3Gate step.) + V3Active::activeAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("active.tree")); + + // Split single ALWAYS blocks into multiple blocks for better ordering chances + if (v3Global.opt.oSplit()) { + V3Split::splitAlwaysAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("split.tree")); + } + + // Create tracing sample points, before we start eliminating signals + if (v3Global.opt.trace()) { + V3TraceDecl::traceDeclAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("tracedecl.tree")); + } + + // Gate-based logic elimination; eliminate signals and push constant across cell boundaries + // Instant propagation makes lots-o-constant reduction possibilities. + if (v3Global.opt.oGate()) { + V3Gate::gateAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("gate.tree")); + } + + // Reorder assignments in pipelined blocks + if (v3Global.opt.oReorder()) { + V3Split::splitReorderAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("reorder.tree")); + } + + // Create delayed assignments + // This creates lots of duplicate ACTIVES so ActiveTop needs to be after this step + V3Delayed::delayedAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("delayed.tree")); + + // Make Active's on the top level + // Differs from V3Active, because identical clocks may be pushed down to a module and now be identical + V3ActiveTop::activeTopAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("activetop.tree")); + + if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "PreOrder"); + + // Order the code; form SBLOCKs and BLOCKCALLs + V3Order::orderAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("order.tree")); + +#ifndef NEW_ORDERING + // Change generated clocks to look at delayed signals + V3GenClk::genClkAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("genclk.tree")); +#endif + + // Convert sense lists into IF statements. + V3Clock::clockAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("clock.tree")); + + // Cleanup any dly vars or other temps that are simple assignments + if (v3Global.opt.oLife()) { + V3Life::lifeAll(v3Global.rootp()); + } + if (v3Global.opt.oLifePost()) { + V3LifePost::lifepostAll(v3Global.rootp()); + } + if (v3Global.opt.oLife() || v3Global.opt.oLifePost()) { + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("life.tree")); + } + + // Remove unused vars + V3Const::constifyAll(v3Global.rootp()); + V3Dead::deadifyAll(v3Global.rootp(), false); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); + +#ifndef NEW_ORDERING + // Detect change loop + V3Changed::changedAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("changed.tree")); +#endif + + // Create tracing logic, since we ripped out some signals the user might want to trace + // Note past this point, we presume traced variables won't move between CFuncs + // (It's OK if untraced temporaries move around, or vars "effectively" activate the same way.) + if (v3Global.opt.trace()) { + V3Trace::traceAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("trace.tree")); + } + + if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "Scoped"); + + // Remove scopes; make varrefs/funccalls relative to current module + V3Descope::descopeAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("descope.tree")); + + //--MODULE OPTIMIZATIONS-------------- + + // Move BLOCKTEMPS from class to local variables + if (v3Global.opt.oLocalize()) { + V3Localize::localizeAll(v3Global.rootp()); + } + + // Icache packing; combine common code in each module's functions into subroutines + if (v3Global.opt.oCombine()) { + V3Combine::combineAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("combine.tree")); + } + + V3Error::abortIfErrors(); + + //--GENERATION------------------ + + // Remove unused vars + V3Const::constifyAll(v3Global.rootp()); + V3Dead::deadifyAll(v3Global.rootp(), true); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); + + // Here down, widthMin() is the Verilog width, and width() is the C++ width + // Bits between widthMin() and width() are irrelevant, but may be non zero. + v3Global.assertWidthsSame(false); + + // Make all operations a multiple of 32 bits + V3Clean::cleanAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("clean.tree")); + + // Move wide constants to BLOCK temps. + V3Premit::premitAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("premit.tree")); + + // Expand macros and wide operators into C++ primitives + if (v3Global.opt.oExpand()) { + V3Expand::expandAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("expand.tree")); + } + + // Propagate constants across WORDSEL arrayed temporaries + if (v3Global.opt.oSubst()) { + // Constant folding of expanded stuff + V3Const::constifyCpp(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree")); + V3Subst::substituteAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("subst.tree")); + } + if (v3Global.opt.oSubstConst()) { + // Constant folding of substitutions + V3Const::constifyCpp(v3Global.rootp()); + + V3Dead::deadifyAll(v3Global.rootp(), true); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("dead.tree")); + } + + // Fix very deep expressions + V3Depth::depthAll(v3Global.rootp()); + //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("depth.tree")); + + // Branch prediction + V3Branch::branchAll(v3Global.rootp()); + + // Add C casts when longs need to become long-long and vice-versa + // Note depth may insert something needing a cast, so this must be last. + V3Cast::castAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("cast.tree")); + + V3Error::abortIfErrors(); + + // Output the text + V3EmitC::emitcSyms(); + V3EmitC::emitcTrace(); + V3EmitC::emitc(); + + // Statistics + if (v3Global.opt.stats()) { + V3Stats::statsFinalAll(v3Global.rootp()); + V3Stats::statsReport(); + } + + // Makefile must be after all other emitters + V3EmitMk::emitmk(v3Global.rootp()); +} + +//###################################################################### + +int main(int argc, char** argv, char** env) { + // General initialization + ios::sync_with_stdio(); + + time_t randseed; + time(&randseed); + srand( (int) randseed); + + // Preprocessor + V3PreShell::boot(env); + + // Command option parsing + string argString = V3Options::argString(argc-1, argv+1); + v3Global.opt.parseOpts(new FileLine("COMMAND_LINE",0), argc-1, argv+1); + if (v3Global.opt.coverage() && !v3Global.opt.systemPerl()) { + v3fatal("Unsupported: Coverage analysis requires --sp output."); + } + if (!v3Global.opt.outFormatOk() && !v3Global.opt.preprocOnly()) { + v3fatal("verilator: Need --cc, --sc, --sp or --E option"); + } + + V3Error::abortIfErrors(); + + // Can we skip doing everything if times are ok? + V3File::addSrcDepend(v3Global.opt.bin()); + if (v3Global.opt.skipIdentical() + && !v3Global.opt.preprocOnly() + && V3File::checkTimes(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__verFiles.dat", argString)) { + UINFO(1,"--skip-identical: No change to any source files, exiting\n"); + exit(0); + } + + // Internal tests (after option parsing as need debug() setting) + V3Graph::test(); + + //--FRONTEND------------------ + + // Cleanup + mkdir(v3Global.opt.makeDir().c_str(), 0777); + string cleanFilename = "/bin/rm -rf "+v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_*.tree"; + system(cleanFilename.c_str()); + cleanFilename = "/bin/rm -rf "+v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_*.dot"; + system(cleanFilename.c_str()); + cleanFilename = "/bin/rm -rf "+v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_*.txt"; + system(cleanFilename.c_str()); + + // Read first filename + v3Global.readFiles(); + //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("parse.tree")); + + // Link, etc, if needed + if (!v3Global.opt.preprocOnly()) { + process(); + } + + // Final steps + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree",99)); + V3Error::abortIfErrors(); + + if (v3Global.opt.depend()) { + V3File::writeDepend(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__ver.d"); + } + if (v3Global.opt.skipIdentical() || v3Global.opt.depend()) { + V3File::writeTimes(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__verFiles.dat", argString); + } + +#ifdef VL_LEAK_CHECKS + // Cleanup memory for valgrind leak analysis + v3Global.clear(); +#endif + + UINFO(1,"Done, Exiting...\n"); +} diff --git a/src/astgen b/src/astgen new file mode 100755 index 000000000..b935358fa --- /dev/null +++ b/src/astgen @@ -0,0 +1,529 @@ +#!/usr/bin/perl -w +#$Id$ +###################################################################### +# +# Copyright 2002-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 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 Perl Artistic License +# along with this module; see the file COPYING. If not, see +# www.cpan.org +# +###################################################################### + +#require 5.006_001; +use Getopt::Long; +use IO::File; +use Pod::Usage; +use strict; +use vars qw ($Debug @Types %Classes); + +#====================================================================== +# main + +$Debug = 0; +my $opt_classes; +my @Opt_Cpt; +my @Opt_I; +Getopt::Long::config ("pass_through", "no_auto_abbrev"); +if (! GetOptions ( + "help" => \&usage, + "debug" => \&debug, + "classes!" => \$opt_classes, + "<>" => \¶meter, + )) { + usage(); +} + +read_types("$Opt_I[0]/V3Ast.h"); +read_types("$Opt_I[0]/V3AstNodes.h"); +if ($opt_classes) { + write_classes("V3Ast__gen_classes.h"); + write_visitor("V3Ast__gen_visitor.h"); + write_intf("V3Ast__gen_interface.h"); + write_impl("V3Ast__gen_impl.h"); + write_types("V3Ast__gen_types.h"); +} +foreach my $cpt (@Opt_Cpt) { + Cpt::process(in_filename=>"$Opt_I[0]/${cpt}.cpp", out_filename=>"${cpt}__gen.cpp"); +} + +#---------------------------------------------------------------------- + +sub usage { + print '$Id$ ', "\n"; + pod2usage(-verbose=>2, -exitval => 2); + exit (1); +} + +sub debug { + $Debug = 1; +} + +sub parameter { + my $param = shift; + if ($param =~ /^-+I(\S+)/) { + push @Opt_I, $1; + } elsif ($param =~ s/\.cpp$//) { + push @Opt_Cpt, $param; + } else { + die "%Error: Unknown parameter: $param,"; + } +} + +####################################################################### + +sub read_types { + my $filename = shift; + + my $fh = IO::File->new($filename) or die "%Error: $! $filename,"; + while (defined (my $line = $fh->getline())) { + $line =~ s/\/\/.*$//; + next if $line =~ /^\s*$/; + if ($line =~ /^\s*(class|struct)\s*(\S+)/) { + my $class = $2; + my $inh = ""; + $inh = $1 if ($line =~ /:\s*public\s+(\S+)/); + print "class $class : $inh\n" if $Debug; + $inh = "" if $class eq "AstNode"; + if ($inh =~ /Ast/ || $class eq "AstNode") { + $class =~ s/^Ast//; + $inh =~ s/^Ast//; + $Classes{$class} = $inh; + } + } + } +} + +sub open_file { + my $filename = shift; + my $fh = IO::File->new($filename,"w") or die "%Error: $! $filename,"; + print $fh "// Generated by astgen // -*- C++ -*-\n"; + return $fh; +} + +#---------------------------------------------------------------------- + +sub subclasses_of { + my $type = shift; + + my @cllist; + for (my $subclass = $::Classes{$type}; $subclass; ) { + push @cllist, $subclass; + $subclass = $::Classes{$subclass}; + } + + return (reverse @cllist); +} + +#---------------------------------------------------------------------- + +sub write_classes { + my $fh = open_file(@_); + foreach my $type (sort (keys %Classes)) { + printf $fh "class %-20s // ", "Ast${type};"; + foreach my $subclass (subclasses_of($type)) { + printf $fh "Ast%-12s ",$subclass; + } + printf $fh "\n"; + } + $fh->close(); +} + +sub write_visitor { + my $fh = open_file(@_); + foreach my $type (sort (keys %Classes)) { + my $base = $Classes{$type}; + if ($base) { + printf $fh " virtual void visit(Ast${type}* nodep, AstNUser* vup) { visit((Ast${base}*)(nodep),vup); }\n"; + } else { + printf $fh " virtual void visit(Ast${type}*, AstNUser*) = 0;\n"; + } + } + $fh->close(); +} + +sub write_intf { + my $fh = open_file(@_); + foreach my $type (sort (keys %Classes)) { + next if $type eq "Node"; # Special, just a return (this); + printf $fh " Ast%-16s cast${type}();\n" + ,$type."*"; + } + $fh->close(); +} + +sub write_impl { + my $fh = open_file(@_); + foreach my $type (sort (keys %Classes)) { + next if $type eq "Node"; # Special, just a return (this); + printf $fh "inline Ast%-16s AstNode::cast${type}() { return (dynamic_cast(this)); }\n" + ,$type."*"; + } + $fh->close(); +} + +sub write_types { + my $fh = open_file(@_); + + printf $fh " enum en {\n"; + foreach my $type (sort (keys %Classes)) { + next if $type =~ /^Node/; + print $fh "\t",uc $type,",\n"; + } + printf $fh "\t_ENUM_END\n"; + printf $fh " };\n"; + printf $fh " const char* ascii() const {\n"; + printf $fh " const char* names[] = {\n"; + foreach my $type (sort (keys %Classes)) { + next if $type =~ /^Node/; + print $fh "\t\"", uc $type, "\",\n"; + } + printf $fh "\t\"_ENUM_END\"\n"; + printf $fh " };\n"; + printf $fh " return names[m_e];\n"; + printf $fh " };\n"; + $fh->close(); +} + +####################################################################### + +package Cpt; + +sub error { + my $self = shift; + my $txt = join('', @_); + die "%Error: $self->{in_filename}:$self->{in_linenum}: $txt\n"; +} + +sub print { + my $self = shift; + my $txt = join('', @_); + push @{$self->{out_lines}}, $txt; +} + +sub output_func { + my $self = shift; + my $func = shift; + push @{$self->{out_lines}}, $func; +} + +sub _output_line { + my $self = shift; + $self->print("#line ",$self->{out_linenum}+2," \"$self->{out_filename}\"\n"); +} + +sub process { + my $self = { + in_filename => undef, + out_filename => undef, + out_lines => [], + out_linenum => 1, + @_, + }; + bless $self, __PACKAGE__; + + my $ln = 1; + my $didln; + + # Read the file and parse into list of functions that generate output + my $fhi = IO::File->new($self->{in_filename}) or die "%Error: $! $self->{in_filename},"; + while (defined(my $line = $fhi->getline)) { + if (!$didln) { + $self->print("#line $. \"$self->{in_filename}\"\n"); + $didln = 1; + } + if ($line =~ /^\s+(TREE.*)$/) { + my $func = $1; + $self->{in_linenum} = $.; + $self->print("//$line"); + $self->output_func(sub{my $self=shift; $self->_output_line(); }); + $self->tree_line ($func); + $didln = 0; + } + elsif ($line !~ /^\s*\/[\/\*]\s*TREE/ + && $line =~ /\s+TREE/) { + $self->error("Unknown astgen line: $line"); + } + else { + $self->print($line); + } + } + $fhi->close; + + # Put out the resultant file, if the list has a reference to a + # function, then call that func to generate output + my $fho = ::open_file($self->{out_filename}); + my @togen = @{$self->{out_lines}}; + foreach my $line (@togen) { + if (ref $line) { + $self->{out_lines} = []; + &$line($self); + } else { + $self->{out_lines} = [$line]; + } + foreach my $out (@{$self->{out_lines}}) { + $self->{out_linenum}++ while ($out =~ /\n/smg); + print $fho $out; + } + } + $fho->close; +} + +sub tree_line { + my $self = shift; + my $func = shift; + + $func =~ s!\s*//.*$!!; + $func =~ s!\s*;\s*$!!; + + if ($func =~ /TREEOP(1?)(V?)\s*\(\s* \"([^\"]*)\" \s*,\s* \"([^\"]*)\" \s*\)/sx) { + my $order = $1; my $cpp = $2; my $from = $3; my $to = $4; + #$self->print("// $from $to\n"); + if (!$self->{did_out_tree}) { + $self->{did_out_tree} = 1; + $self->output_func(sub{ my $self=shift; + $self->tree_match(); + $self->tree_base(); + }); + } + $from =~ /Ast([a-zA-Z0-9]+)\s*\{(.*)\}\s*$/ + or $self->error("Can't parse from function: $func"); + my $type = $1; + my $subnodes = $2; + (::subclasses_of($type)) or $self->error("Unknown AstNode type: $type: in $func"); + + my $mif = ""; + $mif = "!m_cpp" if $cpp; + $subnodes =~ s/,,/__ESCAPEDCOMMA__/g; + foreach my $subnode (split /\s*,\s*/, $subnodes) { + $subnode =~ s/__ESCAPEDCOMMA__/,/g; + next if $subnode =~ /^\$([a-z0-9]+)$/gi; # "$lhs" is just a comment that this op has a lhs + $mif .= " && " if $mif; + my $subnodeif = $subnode; + $subnodeif =~ s/\$([a-zA-Z0-9]+)\.([a-zA-Z0-9]+)$/nodep->$1()->$2()/g; + $subnodeif = add_nodep($subnodeif); + $mif .= $subnodeif; + } + + my $exec_func = treeop_exec_func($self, $to); + + $self->{treeop}{$type} ||= []; + my $n = $#{$self->{treeop}{$type}} + 1; + my $typefunc = { + order => $order, + comment => $func, + match_func => "match_${type}_${n}", + match_if => $mif, + exec_func => $exec_func, + }; + ($typefunc->{uinfo} = $func) =~ s/[ \t\"\{\}]+/ /g; + push @{$self->{treeop}{$type}}, $typefunc; + } + else { + $self->error("Unknown astgen op: $func"); + } +} + +sub add_nodep { + my $str = shift; + $str =~ s/\$([a-zA-Z0-9]+)/nodep->$1()/g; + return $str; +} + +our %_Exec_Syms; +our $_Exec_Nsyms; +sub _exec_syms_recurse { + my $aref = shift; + foreach my $sym (@{$aref}) { + if (ref $sym) { _exec_syms_recurse($sym); } + elsif ($sym =~ /^\$.*/) { + if (!defined $_Exec_Syms{$sym}) { + $_Exec_Syms{$sym} = "arg".($_Exec_Nsyms++)."p"; + } + } + } +} + +sub _exec_new_recurse { + my $aref = shift; + my $out = "new ".$aref->[0]."(nodep->fileline()"; + my $first = 1; + foreach my $sym (@{$aref}) { + if ($first) { $first=0; next; } + $out .= ", "; + if (ref $sym) { $out.=_exec_new_recurse($sym); } + elsif ($sym =~ /^\$.*/) { + $out .= $_Exec_Syms{$sym}; + } else { + $out .= $sym; + } + } + return $out.")"; +} + +sub treeop_exec_func { + my $self = shift; + my $func = shift; + if ($func =~ /^\s*[a-zA-Z0-9]+\s*\(/) { # Function call + $func =~ s/\$([a-zA-Z0-9]+)/nodep->$1()/g; + } + elsif ($func =~ /^\s*Ast([a-zA-Z0-9]+) \s*\{\s* (.*) \s* \}$/x) { + + my $nargs = 0; + my %argnums; # Number for each argument name + + my $aref = undef; # Recursive array with structure to form + my @astack; + my $forming = ""; + my $argtext = $func . "\000"; # EOF character + #print "FF $func\n" if $Debug; + while ($argtext =~ s/^(.)//) { + my $tok = $1; + #print "TOK: $tok $forming\n" if $tok !~ /[a-zA-Z0-9]/; + + if ($tok eq "\000") { + } elsif ($tok =~ /\s+/) { + } elsif ($tok eq "{") { + my $newref = [$forming]; + push @{$aref}, $newref; + push @astack, $aref if $aref; + $aref = $newref; + $forming = ""; + } elsif ($tok eq "}") { + push @{$aref}, $forming if $forming; + $aref = pop @astack; + $aref or $self->error("Too many } in execution function: $func\n"); + $forming = ""; + } elsif ($tok eq ",") { + push @{$aref}, $forming if $forming; + $forming = ""; + } else { + $forming .= $tok; + } + } + ($aref && ref $aref->[0] && !$aref->[1]) or $self->error("Badly formed execution function: $func\n"); + $aref = $aref->[0]; + #use Data::Dumper; print Dumper($aref),"\n"; + + # Assign numbers to each $ symbol + %_Exec_Syms = (); + $_Exec_Nsyms = 0; + _exec_syms_recurse($aref); + + $func = ""; + foreach my $sym (sort {$_Exec_Syms{$a} cmp $_Exec_Syms{$b}} (keys %_Exec_Syms)) { + my $argnp = $_Exec_Syms{$sym}; + my $arg = add_nodep($sym); + $func .= "AstNode* ${argnp} = ${arg}->unlinkFrBack();\n"; + } + + $func .= "AstNode* newp = " . _exec_new_recurse($aref).";\n"; + $func .= "nodep->replaceWith(newp);"; + $func .= "nodep->deleteTree(); nodep=NULL;"; + #print "FF $func\n" if $Debug; + } elsif ($func eq "NEVER") { + $func = "nodep->v3fatalSrc(\"Executing transform that was NEVERed\");"; + } else { + $self->error("Unknown execution function format: $func\n"); + } + return $func; +} + +sub tree_match { + my $self = shift; + $self->print (" // TREEOP functions, each return true if they matched & transformed\n"); + #use Data::Dumper; print Dumper($self); + foreach my $base (sort (keys %{$self->{treeop}})) { + foreach my $typefunc (@{$self->{treeop}{$base}}) { + $self->print(" // Generated by astgen\n"); + $self->print(" bool $typefunc->{match_func}(Ast${base}* nodep) {\n", + "\t// $typefunc->{comment}\n",); + $self->print( "\tif ($typefunc->{match_if}) {\n"); + $self->print( "\t UINFO(7,(void*)(nodep)<<\" $typefunc->{uinfo}\\n\");\n"); + $self->print( "\t $typefunc->{exec_func};\n"); + $self->print( "\t return true;\n"); + $self->print( "\t}\n"); + $self->print( "\treturn false;\n"); + $self->print(" }\n",); + } + } +} + +sub tree_base { + my $self = shift; + $self->print (" // TREEOP visitors, call each base type's match\n"); + $self->print (" // Bottom class up, as more simple transforms are generally better\n"); + foreach my $type (sort (keys %::Classes)) { + my $base = $::Classes{$type}; + my @out_for_type; + foreach my $base (::subclasses_of($type), $type) { + foreach my $typefunc (@{$self->{treeop}{$base}}) { + my @lines = (" if ($typefunc->{match_func}(nodep)) return;\n",); + if ($typefunc->{order}) { + unshift @out_for_type, @lines; # TREEOP1's go in front of others + } else { + push @out_for_type, @lines; + } + } + } + $self->print(" // Generated by astgen\n", + " virtual void visit(Ast${type}* nodep, AstNUser*) {\n", + " nodep->iterateChildren(*this);\n", + @out_for_type, + " }\n",) if $out_for_type[0]; + } +} + +####################################################################### +package main; +__END__ + +=pod + +=head1 NAME + +astgen - Generate V3Ast headers to reduce C++ code duplication + +=head1 SYNOPSIS + + astgen + +=head1 DESCRIPTION + +Generates several files for Verilator compilations. + +=head1 ARGUMENTS + +=over 4 + +=item --help + +Displays this message and program version and exits. + +=item --classes + +Makes class declaration files + +=back + +=head1 SEE ALSO + +=head1 AUTHORS + +Wilson Snyder + +=cut + +###################################################################### +### Local Variables: +### compile-command: "./astgen " +### End: diff --git a/src/config.h.in b/src/config.h.in new file mode 100644 index 000000000..6bddcfad5 --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,70 @@ +// $Id$ -*- C++ -*- +//************************************************************************* +// DESCRIPTION: Verilator: Configure source; system configuration +// +// This file is part of Verilator. +// +// Author: Wilson Snyder or +// +// 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. +// +//************************************************************************* + +//********************************************************************** +//**** Version and host name + +#define DTVERSION "Verilator 3.600 08/26/2006" + +//********************************************************************** +//**** Functions +//* Set define if we have function: strdup (string) +#define HAVE_STRDUP 0 + +//********************************************************************** +//**** Headers +// Set define if we have header: +#define HAVE_FCNTL_H 0 + +// Set define if we have header: +#define HAVE_MATH_H 1 + +// Set define if we have header: +#define HAVE_UNISTD_H 0 + +// Set define if we have header: +#define HAVE_SYS_TIME_H 0 + +// Set define if we have header: +#define HAVE_SYS_UN_H 0 + +// Set define if we have header: +#define HAVE_STDINT_H 0 + +// Set define if we have header: +#define HAVE_MINGW_STDINT_H 0 + +//********************************************************************** +//**** Compile options + +#include +#include +#include + +using namespace std; + +//********************************************************************** +//**** OS and compiler specifics + +#include "verilatedos.h" + diff --git a/src/config_rev.pl b/src/config_rev.pl new file mode 100755 index 000000000..3b5a1ffc3 --- /dev/null +++ b/src/config_rev.pl @@ -0,0 +1,32 @@ +#!/usr/bin/perl -w +#$Id$ +###################################################################### +# +# 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. +# +# 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 Perl Artistic License +# along with this module; see the file COPYING. If not, see +# www.cpan.org +# +###################################################################### + +# DESCRIPTION: Query's subversion to get version number + +my $dir = $ARGV[0]; defined $dir or die "%Error: No directory argument,"; +chdir $dir; + +my $data = `svn info`; +if ($data !~ /\nRevision:\s*(\d+)/) { + die "%Error: No svn info revision found,"; +} +my $rev = $1; +print "static int DTVERSION_rev = $rev;\n"; diff --git a/src/flexfix b/src/flexfix new file mode 100755 index 000000000..7d44071ab --- /dev/null +++ b/src/flexfix @@ -0,0 +1,32 @@ +#!/usr/bin/perl -w +#$Id$ +###################################################################### +# +# Copyright 2002-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 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 Perl Artistic License +# along with this module; see the file COPYING. If not, see +# www.cpan.org +# +###################################################################### + +# DESCRIPTION: Edits flex output to get around Redhat 8.0 broken flex. + +foreach my $line () { + # Fix flex 2.5.4 namespace omission + $line =~ s/^class istream;/\#include \nusing namespace std;\n/; + # Fix flex 2.5.31 redefinition + $line =~ s!(\#define\s+yyFlexLexer\s+yyFlexLexer)!//flexfix: $1!g; + # Fix flex 2.5.1 yytext_ptr undef + $line =~ s!(\#undef\s+yytext_ptr)!//flexfix: $1!g; + print "$line"; +} diff --git a/src/verilog.l b/src/verilog.l new file mode 100644 index 000000000..9560de1dd --- /dev/null +++ b/src/verilog.l @@ -0,0 +1,570 @@ +/* $Id$ -*- C++ -*- */ +/************************************************************************** + * DESCRIPTION: Verilator: Flex input file + * + * 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. + * + **************************************************************************/ + +%option interactive c++ stack noyywrap +%{ +/* %option nodefault */ +/* $Id$ */ + +#include +#include +#include "V3Read.h" +#include "V3Number.h" +#include "y.tab.h" + +extern void yyerror(char*); +extern void yyerrorf(const char* format, ...); + +//====================================================================== + +#define NEXTLINE() {V3Read::incLineno();} +#define CRELINE() (V3Read::copyOrSameFileLine()) + +void V3Read::ppline (const char* textp) { + // Handle `line directive + fileline()->lineDirective(textp); +} + +void V3Read::verilatorCmtLint(const char* textp, bool warnOff) { + const char* sp = textp; + while (*sp && !isspace(*sp)) sp++; while (*sp && isspace(*sp)) sp++; + while (*sp && !isspace(*sp)) sp++; while (*sp && isspace(*sp)) sp++; + string msg = sp; + string::size_type pos; + if ((pos=msg.find("*")) != string::npos) { msg.erase(pos); } + if (!(V3Read::fileline()->warnOff(msg, warnOff))) { + yyerrorf("Unknown verilator lint message code: %s, in %s",msg.c_str(), textp); + } +} +void V3Read::verilatorCmtBad(const char* textp) { + yyerrorf("Unknown verilator comment: %s",textp); +} + +// See V3Read.cpp +//void V3Read::stateExitPsl() { BEGIN VLG; } +//void V3Read::statePushVlg() { yy_push_state(VLG); } +//void V3Read::statePop() { yy_pop_state(); } + +//====================================================================== + +void yyerror(char* errmsg) { + yyerrorf("%s",errmsg); +} + +void yyerrorf(const char* format, ...) { + char msg[1024]; + + va_list ap; + va_start(ap,format); + vsprintf(msg,format,ap); + va_end(ap); + + V3Read::fileline()->v3error(msg); +} + +//====================================================================== +%} + +%e 2000 +%p 5000 +%n 2500 +%k 1000 +%a 15000 +%o 25000 + +%s VLG PSL STRING SYSCHDR SYSCINT SYSCIMP SYSCIMPH SYSCCTOR IGNORE + +ws [ \t\f\r]+ + /* identifier */ +id [a-zA-Z_][a-zA-Z0-9_$]* + /* escaped identifier */ +escid \\[^ \t\f\r\n]+ + +%% + +.|\n {BEGIN VLG; yyless(0); } + +{ws} ; /* ignore white-space */ +\n {NEXTLINE();} /* Count line numbers */ +always {yylval.fileline = CRELINE(); return yALWAYS;} +always_comb {yylval.fileline = CRELINE(); return yALWAYS;} +always_ff {yylval.fileline = CRELINE(); return yALWAYS;} +always_latch {yylval.fileline = CRELINE(); return yALWAYS;} +and {yylval.fileline = CRELINE(); return yAND;} +assign {yylval.fileline = CRELINE(); return yASSIGN;} +begin {yylval.fileline = CRELINE(); return yBBEGIN;} +buf {yylval.fileline = CRELINE(); return yBUF;} +case {yylval.fileline = CRELINE(); return yCASE;} +casex {yylval.fileline = CRELINE(); return yCASEX;} +casez {yylval.fileline = CRELINE(); return yCASEZ;} +default {yylval.fileline = CRELINE(); return yDEFAULT;} +defparam {yylval.fileline = CRELINE(); return yDEFPARAM;} +else {yylval.fileline = CRELINE(); return yELSE;} +end {yylval.fileline = CRELINE(); return yBEND;} +endcase {yylval.fileline = CRELINE(); return yENDCASE;} +endfunction {yylval.fileline = CRELINE(); return yENDFUNCTION;} +endgenerate {yylval.fileline = CRELINE(); return yENDGENERATE;} +endmodule {yylval.fileline = CRELINE(); return yENDMODULE;} +endspecify {yylval.fileline = CRELINE(); return yENDSPECIFY;} +endtask {yylval.fileline = CRELINE(); return yENDTASK;} +final {yylval.fileline = CRELINE(); return yFINAL;} +for {yylval.fileline = CRELINE(); return yFOR;} +function {yylval.fileline = CRELINE(); return yFUNCTION;} +generate {yylval.fileline = CRELINE(); return yGENERATE;} +genvar {yylval.fileline = CRELINE(); return yGENVAR;} +if {yylval.fileline = CRELINE(); return yIF;} +initial {yylval.fileline = CRELINE(); return yINITIAL;} +inout {yylval.fileline = CRELINE(); return yINOUT;} +input {yylval.fileline = CRELINE(); return yINPUT;} +integer {yylval.fileline = CRELINE(); return yINTEGER;} +localparam {yylval.fileline = CRELINE(); return yLOCALPARAM;} +macromodule {yylval.fileline = CRELINE(); return yMODULE;} +module {yylval.fileline = CRELINE(); return yMODULE;} +nand {yylval.fileline = CRELINE(); return yNAND;} +negedge {yylval.fileline = CRELINE(); return yNEGEDGE;} +nor {yylval.fileline = CRELINE(); return yNOR;} +not {yylval.fileline = CRELINE(); return yNOT;} +or {yylval.fileline = CRELINE(); return yOR;} +output {yylval.fileline = CRELINE(); return yOUTPUT;} +parameter {yylval.fileline = CRELINE(); return yPARAM;} +posedge {yylval.fileline = CRELINE(); return yPOSEDGE;} +reg {yylval.fileline = CRELINE(); return yREG;} +scalared {yylval.fileline = CRELINE(); return ySCALARED;} +signed {yylval.fileline = CRELINE(); return ySIGNED;} +specify {yylval.fileline = CRELINE(); return ySPECIFY;} +specparam {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +supply0 {yylval.fileline = CRELINE(); return ySUPPLY0;} +supply1 {yylval.fileline = CRELINE(); return ySUPPLY1;} +task {yylval.fileline = CRELINE(); return yTASK;} +tri {yylval.fileline = CRELINE(); return yTRI;} +vectored {yylval.fileline = CRELINE(); return yVECTORED;} +wire {yylval.fileline = CRELINE(); return yWIRE;} +xnor {yylval.fileline = CRELINE(); return yXNOR;} +xor {yylval.fileline = CRELINE(); return yXOR;} +"$bits" {yylval.fileline = CRELINE(); return yD_BITS;} +"$c"[0-9]* {yylval.fileline = CRELINE(); return yD_C;} +"$countones" {yylval.fileline = CRELINE(); return yD_COUNTONES;} +"$display" {yylval.fileline = CRELINE(); return yD_DISPLAY;} +"$fclose" {yylval.fileline = CRELINE(); return yD_FCLOSE;} +"$fdisplay" {yylval.fileline = CRELINE(); return yD_FDISPLAY;} +"$finish" {yylval.fileline = CRELINE(); return yD_FINISH;} +"$fopen" {yylval.fileline = CRELINE(); return yD_FOPEN;} +"$fullskew" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$fwrite" {yylval.fileline = CRELINE(); return yD_FWRITE;} +"$hold" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$isunknown" {yylval.fileline = CRELINE(); return yD_ISUNKNOWN;} +"$nochange" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$onehot" {yylval.fileline = CRELINE(); return yD_ONEHOT;} +"$onehot0" {yylval.fileline = CRELINE(); return yD_ONEHOT0;} +"$period" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$realtime" {yylval.fileline = CRELINE(); return yD_TIME;} +"$recovery" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$recrem" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$removal" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$setup" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$setuphold" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$signed" {yylval.fileline = CRELINE(); return yD_SIGNED;} +"$skew" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$stop" {yylval.fileline = CRELINE(); return yD_STOP;} +"$time" {yylval.fileline = CRELINE(); return yD_TIME;} +"$timeskew" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$unsigned" {yylval.fileline = CRELINE(); return yD_UNSIGNED;} +"$width" {yylval.fileline = CRELINE(); return yTIMINGSPEC;} +"$write" {yylval.fileline = CRELINE(); return yD_WRITE;} +"$"[a-zA-Z_$]+ {yyerrorf("Unsupported or unknown PLI call: %s",yytext);} + + /*PSL Implemented */ +psl { BEGIN PSL; yylval.fileline = CRELINE(); return yPSL; } +psl { ; } // 'psl' may occur in middle of statement, so easier just to suppress + +assert {yylval.fileline = CRELINE(); return yASSERT;} +assume {yylval.fileline = CRELINE(); return yASSERT;} //==assert +before_! {yyerrorf("Illegal syntax, use before!_ instead of %s",yytext);} +clock {yylval.fileline = CRELINE(); return yCLOCK;} +countones {yylval.fileline = CRELINE(); return yD_COUNTONES;} +cover {yylval.fileline = CRELINE(); return yCOVER;} +isunknown {yylval.fileline = CRELINE(); return yD_ISUNKNOWN;} +onehot {yylval.fileline = CRELINE(); return yD_ONEHOT; } +onehot0 {yylval.fileline = CRELINE(); return yD_ONEHOT0; } +until_! {yyerrorf("Illegal syntax, use until!_ instead of %s",yytext);} +report {yylval.fileline = CRELINE(); return yREPORT; } +true {yylval.fileline = CRELINE(); return yTRUE; } + + /*Verilog Reserved*/ +bufif0 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +bufif1 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +cmos {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +deassign {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +disable {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +endprimitive {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +endtable {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +event {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +force {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +forever {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +fork {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +highz0 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +highz1 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +join {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +large {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +medium {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +nmos {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +notif0 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +notif1 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +pmos {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +primitive {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +pulldown {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +pullup {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +pull0 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +pull1 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +rcmos {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +real {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +realtime {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +release {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +repeat {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +rnmos {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +rpmos {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +rtran {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +rtranif0 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +rtranif1 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +small {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +strong0 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +strong1 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +table {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +time {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +tran {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +tranif0 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +tranif1 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +triand {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +trior {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +trireg {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +tri0 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +tri1 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +wait {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +wand {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +weak0 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +weak1 {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +while {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} +wor {yyerrorf("Unsupported: Verilog reserved word not implemented: %s",yytext);} + + /* Verilog 2001 */ +automatic {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +cell {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +config {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +design {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +edge {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +endconfig {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +ifnone {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +instance {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +liblist {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +noshowcancelled {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +pulsestyle_ondetect {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +pulsestyle_onevent {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +showcancelled {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +strength {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +unsigned {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} +use {yyerrorf("Unsupported: Verilog 2001 reserved word not implemented: %s",yytext);} + + /* System Verilog */ +alias {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +assert_strobe {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +bind {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +bit {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +break {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +byte {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +chandle {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +class {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +clocking {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +constraint {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +context {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +continue {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +dist {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +do {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +endcass {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +endclocking {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +endinterface {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +endprogram {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +endproperty {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +endsequence {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +enum {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +export {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +extends {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +extern {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +first_match {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +forkjoin {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +iff {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +import {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +inside {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +int {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +interface {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +intersect {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +join_any {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +join_none {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +local {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +logic {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +longint {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +modport {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +new {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +null {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +packed {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +priority {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +program {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +protected {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +pure {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +rand {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +randc {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +ref {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +shortint {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +shortreal {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +solve {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +static {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +string {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +struct {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +super {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +this {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +throughout {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +timeprecision {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +timeunit {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +type {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +typedef {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +unique {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +var {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +virtual {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +void {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +wait_order {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} +with {yyerrorf("Unsupported: SystemVerilog reserved word not implemented: %s",yytext);} + + /* Verilog, also as PSL below PSL */ +assert {yyerrorf("Unsupported: SystemVerilog reserved word not implemented in non-PSL context: %s",yytext);} +before {yyerrorf("Unsupported: SystemVerilog reserved word not implemented in non-PSL context: %s",yytext);} +const {yyerrorf("Unsupported: SystemVerilog reserved word not implemented in non-PSL context: %s",yytext);} +cover {yyerrorf("Unsupported: SystemVerilog reserved word not implemented in non-PSL context: %s",yytext);} +property {yyerrorf("Unsupported: SystemVerilog reserved word not implemented in non-PSL context: %s",yytext);} +sequence {yyerrorf("Unsupported: SystemVerilog reserved word not implemented in non-PSL context: %s",yytext);} +union {yyerrorf("Unsupported: SystemVerilog reserved word not implemented in non-PSL context: %s",yytext);} +within {yyerrorf("Unsupported: SystemVerilog reserved word not implemented in non-PSL context: %s",yytext);} + + /* PSL reserved */ + /*A {yyerrorf("Unsupported: PSL branching reserved word not implemented: %s",yytext);} */ + /*AF {yyerrorf("Unsupported: PSL branching reserved word not implemented: %s",yytext);} */ + /*AG {yyerrorf("Unsupported: PSL branching reserved word not implemented: %s",yytext);} */ + /*AX {yyerrorf("Unsupported: PSL branching reserved word not implemented: %s",yytext);} */ + /*E {yyerrorf("Unsupported: PSL branching reserved word not implemented: %s",yytext);} */ + /*EF {yyerrorf("Unsupported: PSL branching reserved word not implemented: %s",yytext);} */ + /*EG {yyerrorf("Unsupported: PSL branching reserved word not implemented: %s",yytext);} */ + /*EX {yyerrorf("Unsupported: PSL branching reserved word not implemented: %s",yytext);} */ + /*F {yylval.fileline = CRELINE(); return yEVENTUALLYB; } */ + /*G {yylval.fileline = CRELINE(); return yALWAYS; } */ + /*U {yylval.fileline = CRELINE(); return yUNTILB; } */ + /*W {yylval.fileline = CRELINE(); return yUNTIL; } */ + /*X {yylval.fileline = CRELINE(); return yNEXT; } */ + /*X! {yylval.fileline = CRELINE(); return yNEXTB; } */ +%for {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +%if {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +abort {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +assume_guarantee {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} //Unsup in other tools +before {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +before! {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +before!_ {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +before_ {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +boolean {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +const {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +endpoint {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +eventually! {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +fairness {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} //Unsup in other tools +fell {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +forall {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} //Unsup in other tools +in {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +inf {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +inherit {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} //Unsup in other tools +never {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next! {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next_a {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next_a! {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next_e {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next_e! {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next_event {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next_event! {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next_event_a {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next_event_a! {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next_event_e {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +next_event_e! {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +prev {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +property {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +restrict {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +restrict_guarantee {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} //Unsup in other tools +rose {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +sequence {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +stable {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +strong {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} //Unsup in other tools +union {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +until {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +until! {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +until!_ {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +until_ {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +vmode {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} //Unsup in other tools +vprop {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} //Unsup in other tools +vunit {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} +within {yyerrorf("Unsupported: PSL reserved word not implemented: %s",yytext);} + + /* Converted from //{cmt}verilator ...{cmt} by vpp */ +"/*verilator clock_enable*/" {yylval.fileline = CRELINE(); return yVL_CLOCK_ENABLE;} +"/*verilator coverage_block_off*/" {yylval.fileline = CRELINE(); return yVL_COVER_OFF;} +"/*verilator full_case*/" {yylval.fileline = CRELINE(); return yVL_FULL_CASE;} +"/*verilator inline_module*/" {yylval.fileline = CRELINE(); return yVL_INLINE_MODULE;} +"/*verilator no_inline_module*/" {yylval.fileline = CRELINE(); return yVL_NO_INLINE_MODULE;} +"/*verilator one_cold*/" {yylval.fileline = CRELINE(); return yVL_ONE_COLD;} +"/*verilator one_hot*/" {yylval.fileline = CRELINE(); return yVL_ONE_HOT;} +"/*verilator parallel_case*/" {yylval.fileline = CRELINE(); return yVL_PARALLEL_CASE;} +"/*verilator public*/" {yylval.fileline = CRELINE(); return yVL_PUBLIC;} +"/*verilator public_module*/" {yylval.fileline = CRELINE(); return yVL_PUBLIC_MODULE;} +"/*verilator sc_clock*/" {yylval.fileline = CRELINE(); return yVL_CLOCK;} +"/*verilator systemc_clock*/" {yylval.fileline = CRELINE(); return yVL_CLOCK;} +"/*verilator tracing_off*/" {yylval.fileline = CRELINE(); return yVL_TRACING_OFF;} +"/*verilator tracing_on*/" {yylval.fileline = CRELINE(); return yVL_TRACING_ON;} +"/*verilator lint_off"[^*]*"*/" {V3Read::verilatorCmtLint(yytext, true); } +"/*verilator lint_on"[^*]*"*/" {V3Read::verilatorCmtLint(yytext, false); } +"/*"[^*]*"*/" {V3Read::verilatorCmtBad(yytext); } + +"&&" {yylval.fileline = CRELINE(); return yANDAND;} +"||" {yylval.fileline = CRELINE(); return yOROR;} +"<=" {yylval.fileline = CRELINE(); return yLTE;} +">=" {yylval.fileline = CRELINE(); return yGTE;} +"<<<" {yylval.fileline = CRELINE(); return ySLEFT;} +"<<" {yylval.fileline = CRELINE(); return ySLEFT;} +">>>" {yylval.fileline = CRELINE(); return ySSRIGHT;} +">>" {yylval.fileline = CRELINE(); return ySRIGHT;} +"==" {yylval.fileline = CRELINE(); return yEQUAL;} +"!=" {yylval.fileline = CRELINE(); return yNOTEQUAL;} +"===" {yylval.fileline = CRELINE(); return yCASEEQUAL;} +"!==" {yylval.fileline = CRELINE(); return yCASENOTEQUAL;} +"^~" {yylval.fileline = CRELINE(); return yOP_XNOR;} +"~^" {yylval.fileline = CRELINE(); return yOP_XNOR;} +"~&" {yylval.fileline = CRELINE(); return yOP_NAND;} +"~|" {yylval.fileline = CRELINE(); return yOP_NOR;} +"**" {yylval.fileline = CRELINE(); return yPOW;} +"+:" {yylval.fileline = CRELINE(); return yPLUSCOLON;} +"-:" {yylval.fileline = CRELINE(); return yMINUSCOLON;} + +"{" {yylval.fileline = CRELINE(); return yPSL_BRA;} // Avoid parser hitting concatenate. +"}" {yylval.fileline = CRELINE(); return yPSL_KET;} // Avoid parser hitting concatenate. +"->" {yylval.fileline = CRELINE(); return yOP_LOGIF;} +"<->" {yyerrorf("Unsupported: PSL operator not implemented: %s",yytext);} //Unsup in other tools +"[*" {yyerrorf("Unsupported: PSL operator not implemented: %s",yytext);} // yBRA_STAR +"[*]" {yyerrorf("Unsupported: PSL operator not implemented: %s",yytext);} // yBRA_STAR_KET +"[+]" {yyerrorf("Unsupported: PSL operator not implemented: %s",yytext);} // yBRA_PLUS_KET +"[->" {yyerrorf("Unsupported: PSL operator not implemented: %s",yytext);} // yBRA_MINUS_GT +"[->]" {yyerrorf("Unsupported: PSL operator not implemented: %s",yytext);} // yBRA_MINUS_GT_KET +"[=" {yyerrorf("Unsupported: PSL operator not implemented: %s",yytext);} // yBRA_EQ +"|->" {yyerrorf("Unsupported: PSL operator not implemented: %s",yytext);} // yOR_MINUS_GT +"|=>" {yyerrorf("Unsupported: PSL operator not implemented: %s",yytext);} // yOR_EQ_GT + +{escid} { int i; + for (i=0; yytext[i] != 0; i++) + if (!isalnum(yytext[i])) + yytext[i] = '_'; + if (isalpha(yytext[1])) { + yylval.strp = V3Read::newString(yytext+1); // +1 to skip the backslash + } else { + yylval.strp = V3Read::newString(yytext); // Need _ as "6..." isn't legal ID + } + return yID; + } + +{id} { yylval.strp = V3Read::newString(yytext); + return yID; + } + +\"[^\"\\]*\" { yylval.strp = V3Read::newString(yytext+1,yyleng-2); + return ySTRING; + } +\" { yy_push_state(STRING); yymore(); } + + +[0-9]*?['']s?[bcodhBCODH][ \t]*[A-Fa-f0-9xXzZ_?]* { + yylval.nump = V3Read::newNumber(V3Read::fileline(),(char*)yytext); + return yINTNUM; + } +[0-9]*?['']s?[01xXzZ] { /* SystemVerilog */ + yylval.nump = V3Read::newNumber(V3Read::fileline(),(char*)yytext); + return yINTNUM; + } +[0-9]+[_0-9]*[ \t]*['']s?[bcodhBCODH]?[ \t]*[A-Fa-f0-9xXzZ_?]* { + yylval.nump = V3Read::newNumber(V3Read::fileline(),(char*)yytext); + return yINTNUM; + } +[0-9]* { yylval.nump = V3Read::newNumber(V3Read::fileline(),(char*)yytext); + return yINTNUM; + } +[-+]?[0-9]+(\.[0-9]+)([eE][-+]?[0-9]+)? { + yylval.cdouble = 0; /* Only for delays, not used yet */ + return yFLOATNUM; + } +[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+) { + yylval.cdouble = 0; /* Only for delays, not used yet */ + return yFLOATNUM; + } + +"`timescale"{ws}+[^\n]* {} +"`line"{ws}+[^\n]*\n {V3Read::ppline(yytext);} + + /************************************************************************/ + /* STRINGS */ +\n { yyerror("Unterminated string\n"); } +\r ; +[^\"\\]* { yymore(); } +\\. { yymore(); } +\" { yy_pop_state(); + yylval.strp = V3Read::newString(yytext+1,yyleng-2); + return ySTRING; } + + /************************************************************************/ + /* Common for all SYSC header states */ + /* OPTIMIZE: we return one per line, make it one for the entire block */ +[ \t]*"`verilog" { BEGIN VLG; } +[ \t]*"`psl" { if (V3Read::optPsl()) { BEGIN PSL; } else { BEGIN IGNORE; } } +[ \t]*"`systemc_header" { BEGIN SYSCHDR; } +[ \t]*"`systemc_ctor" { BEGIN SYSCCTOR; } +[ \t]*"`systemc_interface" { BEGIN SYSCINT; } +[ \t]*"`systemc_implementation" { BEGIN SYSCIMP; } +[ \t]*"`systemc_imp_header" { BEGIN SYSCIMPH; } +"`line"[ \t][^\n]*\n {V3Read::ppline(yytext);} + +[ \t]*[^` \t\n][^\n]*\n { NEXTLINE(); yylval.strp = V3Read::newString(yytext); return ySCHDR;} +[ \t]*[^` \t\n][^\n]*\n { NEXTLINE(); yylval.strp = V3Read::newString(yytext); return ySCINT;} +[ \t]*[^` \t\n][^\n]*\n { NEXTLINE(); yylval.strp = V3Read::newString(yytext); return ySCIMP;} +[ \t]*[^` \t\n][^\n]*\n { NEXTLINE(); yylval.strp = V3Read::newString(yytext); return ySCIMPH;} +[ \t]*[^` \t\n][^\n]*\n { NEXTLINE(); yylval.strp = V3Read::newString(yytext); return ySCCTOR;} +[ \t]*[^` \t\n][^\n]*\n { NEXTLINE(); } + +[ \t]*\n { NEXTLINE(); yymore();} +\r ; + + /************************************************************************/ + /* Default rules - leave last */ + +"`"[a-zA-Z_0-9]+ { yyerrorf("Define or directive not defined: %s",yytext); } + +"//"[^\n]+ { } /* throw away single line comments */ + +. {yylval.fileline = CRELINE(); return yytext[0];} /* return single char ops. */ + +<*>.|\n { yyerrorf("Missing verilog.l rule: Default rule invoked in state %d: %s", YY_START, yytext); } +%% + diff --git a/src/verilog.y b/src/verilog.y new file mode 100644 index 000000000..827b29065 --- /dev/null +++ b/src/verilog.y @@ -0,0 +1,1165 @@ +// $Id$ -*- C++ -*- +//************************************************************************* +// DESCRIPTION: Verilator: Bison grammer file +// +// 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. +// +//************************************************************************* +// Original code here by Paul Wasson and Duane Galbi +//************************************************************************* +//General Hints +//----------------- +// 1) {A,B} <= HI; -> expands to two DISTINCT assignment nodes in AST tree +// +// 2) inv a(in1,out1), b(in2,out2); -> expanded into two AST instance_nodes +// +//************************************************************************* + +%{ +/* $Id$ */ +#include +#include +#include +#include + +#include "V3Read.h" +#include "V3Ast.h" +#include "V3Global.h" + +#define YYERROR_VERBOSE +#define YYMAXDEPTH 500 + +// Pick up new lexer +#define yylex V3Read::yylex +#define PSLUNSUP(what) NULL; yyerror("Unsupported: PSL language feature not implemented"); + +extern void yyerror(char* errmsg); +extern void yyerrorf(const char* format, ...); + +//====================================================================== +// Statics (for here only) + +class V3Parse { +public: + static bool s_impliedDecl; // Allow implied wire declarations + static AstVarType s_varDecl; // Type for next signal declaration (reg/wire/etc) + static AstVarType s_varIO; // Type for next signal declaration (input/output/etc) + static bool s_varSigned; // Signed state for next signal declaration + static AstVar* s_varAttrp; // Current variable for attribute adding + static AstCase* s_caseAttrp; // Current case statement for attribute adding + static AstRange* s_varRangep; // Pointer to range for next signal declaration + static int s_pinNum; // Pin number currently parsing + static string s_instModule; // Name of module referenced for instantiations + static AstPin* s_instParamp; // Parameters for instantiations + static bool s_trace; // Tracing is turned on + + static AstVar* createVariable(FileLine* fileline, string name, AstRange* arrayp); + static AstNode* createSupplyExpr(FileLine* fileline, string name, int value); + static AstText* createTextQuoted(FileLine* fileline, string text); + static string deQuote(FileLine* fileline, string text); +}; + +bool V3Parse::s_impliedDecl = false; +bool V3Parse::s_trace = false; // Set on first module creation +AstVarType V3Parse::s_varDecl = AstVarType::UNKNOWN; +AstVarType V3Parse::s_varIO = AstVarType::UNKNOWN; +bool V3Parse::s_varSigned = false; +AstRange* V3Parse::s_varRangep = NULL; +int V3Parse::s_pinNum = -1; +string V3Parse::s_instModule; +AstPin* V3Parse::s_instParamp = NULL; +AstVar* V3Parse::s_varAttrp = NULL; +AstCase* V3Parse::s_caseAttrp = NULL; + +#define CRELINE() (V3Read::copyOrSameFileLine()) +#define VARRESET() { VARDECL(UNKNOWN); VARIO(UNKNOWN); VARSIGNED(false); VARRANGE(NULL); } +#define VARDECL(type) { V3Parse::s_varDecl = AstVarType::type; } +#define VARIO(type) { V3Parse::s_varIO = AstVarType::type; } +#define VARSIGNED(value) { V3Parse::s_varSigned = value; } +#define VARRANGE(range) { V3Parse::s_varRangep=(range); } +#define INSTPREP(modname,paramsp) { V3Parse::s_impliedDecl = true; V3Parse::s_instModule = modname; V3Parse::s_instParamp = paramsp; } + +//====================================================================== + +class AstSenTree; + +# define NEW_SENTREE_BRANCH(a,b) (new AstSenTree((a),(b))) +%} + +%union { + FileLine* fileline; + V3Number* nump; + string* strp; + int cint; + double cdouble; + + AstNode* nodep; + + AstAssignW* assignwp; + AstBegin* beginp; + AstCase* casep; + AstCaseItem* caseitemp; + AstConst* constp; + AstModule* modulep; + AstPin* pinp; + AstRange* rangep; + AstSenItem* senitemp; + AstSenTree* sentreep; + AstTaskRef* taskrefp; + AstVar* varp; + AstVarRef* varrefp; + AstNodeVarRef* varnodep; +} + +%token yINTNUM +%token yFLOATNUM +%token yID ySTRING +%token ySCHDR ySCINT ySCIMP ySCIMPH ySCCTOR +%token yMODULE yENDMODULE yALWAYS yINITIAL yPOSEDGE yNEGEDGE yBBEGIN yBEND yOR +%token yINPUT yOUTPUT yINOUT yWIRE yTRI yREG yPARAM yLOCALPARAM yDEFPARAM +%token yFUNCTION yENDFUNCTION yTASK yENDTASK +%token yGENVAR yGENERATE yENDGENERATE +%token ySPECIFY yENDSPECIFY yTIMINGSPEC +%token yCASE yCASEX yCASEZ yDEFAULT yENDCASE yIF yELSE +%token yASSIGN yINTEGER yFOR ySUPPLY0 ySUPPLY1 ySIGNED +%token yBUF yNOT yAND yNAND yNOR yXOR yXNOR +%token ySCALARED yVECTORED + +%token yASSERT "assert" +%token yCLOCK "clock" +%token yCOVER "cover" +%token yFINAL "final" +%token yPSL "psl" +%token yREPORT "report" +%token yTRUE "true" + +%token yD_BITS "$bits" +%token yD_C "$c" +%token yD_COUNTONES "$countones" +%token yD_DISPLAY "$display" +%token yD_FCLOSE "$fclose" +%token yD_FDISPLAY "$fdisplay" +%token yD_FINISH "$finish" +%token yD_FOPEN "$fopen" +%token yD_FWRITE "$fwrite" +%token yD_ISUNKNOWN "$isunknown" +%token yD_ONEHOT "$onehot" +%token yD_ONEHOT0 "$onehot0" +%token yD_SIGNED "$signed" +%token yD_STOP "$stop" +%token yD_TIME "$time" +%token yD_UNSIGNED "$unsigned" +%token yD_WRITE "$write" + +%token yVL_CLOCK "/*verilator sc_clock*/" +%token yVL_CLOCK_ENABLE "/*verilator clock_enable*/" +%token yVL_COVER_OFF "/*verilator coverage_block_off*/" +%token yVL_FULL_CASE "/*verilator full_case*/" +%token yVL_INLINE_MODULE "/*verilator inline_module*/" +%token yVL_NO_INLINE_MODULE "/*verilator no_inline_module*/" +%token yVL_ONE_COLD "/*verilator one_cold*/" +%token yVL_ONE_HOT "/*verilator one_hot*/" +%token yVL_PARALLEL_CASE "/*verilator parallel_case*/" +%token yVL_PUBLIC "/*verilator public*/" +%token yVL_PUBLIC_MODULE "/*verilator public_module*/" +%token yVL_TRACING_OFF "/*verilator tracing_off*/" +%token yVL_TRACING_ON "/*verilator tracing_on*/" + +%token yPLUSCOLON "+:" +%token yMINUSCOLON "-:" +%token yPSL_BRA "{" +%token yPSL_KET "}" + +%token ';' '=' ',' '(' '.' '!' '~' '[' '@' + +//******************** +// PSL op precedence +%right yOP_LOGIF yOP_LOGIFF +%right yOR_MINUS_GT yOR_EQ_GT +%left yBRA_STAR yBRA_STAR_KET yBRA_PLUS_KET yBRA_MINUS_GT yBRA_MINUS_GT_KET yBRA_EQ +%left prPSLCLK + +// Verilog op precedence +%left ':' +%left '?' +%left yOROR +%left yANDAND +%left '|' yOP_NOR +%left '^' +%left yOP_XNOR +%left '&' yOP_NAND +%left yEQUAL yNOTEQUAL yCASEEQUAL yCASENOTEQUAL +%left '>' '<' yGTE yLTE +%left ySLEFT ySRIGHT ySSRIGHT +%left '+' '-' +%left '*' '/' '%' +%left yPOW +%left '{' '}' +%left yUNARYARITH +%left yREDUCTION +%left yNEGATION + +%nonassoc yLOWER_THAN_ELSE +%nonassoc yELSE + +// Types are in same order as declarations. +// Naming: +// Trailing E indicates this type may have empty match +%type mheader +%type modportsE portList port +%type v2kPortList v2kPortSig +%type v2kPort ioDecl varDecl +%type modParDecl modParList modParE +%type modItem modItemList modItemListOrNone modOrGenItem +%type genItem genItemList genItemBegin genItemBlock genItemsBlock genCaseList +%type dterm +%type onesig sigId sigIdRange paramId sigList regsig regsigList regSigId +%type netSig netSigList +%type rangeListE regrangeE anyrange rangeList delayrange portrangeE +%type param paramList +%type instnameList instname +%type cellpinList cellpinlist2 cellpinitemE instparamListE +%type defpList defpOne +%type ignoreRangeE +%type sensitivityE +%type senList senitem senitemEdge +%type stmtBlock stmtList stmt stateCaseForIf +%type beginNamed +%type caseStmt +%type caseList +%type casecondList assignList assignOne +%type constExpr exprNoStr expr exprPsl exprStrText +%type eList cateList cStrList +%type pathDotted +%type idVarRef +%type idVarXRef +%type lhIdVarRef +%type lhIdVarXRef +%type taskRef +%type idRanged lhIdRanged +%type idArrayed lhIdArrayed +%type strAsInt strAsText lhConcIdList +%type taskDecl +%type varDeclList funcDecl funcVarList funcVar +%type funcRange +%type gateDecl +%type gateBufList gateNotList gateAndList gateNandList +%type gateOrList gateNorList gateXorList gateXnorList +%type gateBuf gateNot gateAnd gateNand gateOr gateNor gateXor gateXnor +%type gateAndPinList gateOrPinList gateXorPinList + +%type pslStmt pslDir pslDirOne pslProp +%type pslDecl +%type pslSequence pslSere pslExpr + +%start file + +%% +//********************************************************************** +//********************************************************************** +//********************************************************************** +// Feedback to the Lexer + +stateExitPsl: { V3Read::stateExitPsl(); } + ; +statePushVlg: { V3Read::statePushVlg(); } + ; +statePop: { V3Read::statePop(); } + ; + +//********************************************************************** +//********************************************************************** +//********************************************************************** +// Modules + +file: prog + | file prog + ; + +prog: mheader modParE modportsE ';' modItemListOrNone yENDMODULE + { $1->modTrace(V3Parse::s_trace); // Stash for implicit wires, etc + if ($2) $1->addStmtp($2); if ($3) $1->addStmtp($3); if ($5) $1->addStmtp($5); } + ; + +mheader: yMODULE { V3Parse::s_trace=v3Global.opt.trace();} + yID { $$ = new AstModule($1,*$3); $$->inLibrary(V3Read::inLibrary()); + $$->modTrace(v3Global.opt.trace()); + V3Read::rootp()->addModulep($$); } + ; + +modParE: /* empty */ { $$ = NULL; } + | '#' '(' ')' { $$ = NULL; } + | '#' '(' modParList ')' { $$ = $3; } + | '#' '(' modParList ';' ')' { $$ = $3; } + ; + +modParList: modParDecl { $$ = $1; } + | modParList ';' modParDecl { $$ = $1->addNext($3); } + ; + +modportsE: /* empty */ { $$ = NULL; } + | '(' ')' { $$ = NULL; } + | '(' {V3Parse::s_pinNum=1;} portList ')' { $$ = $3; } + | '(' {V3Parse::s_pinNum=1;} v2kPortList ')' { $$ = $3; } + ; + +portList: port { $$ = $1; } + | portList ',' port { $$ = $1->addNext($3); } + ; + +port: yID portrangeE { $$ = new AstPort(CRELINE(),V3Parse::s_pinNum++,*$1); } + ; + +v2kPortList: v2kPort { $$ = $1; } + | v2kPortList ',' v2kPort { $$ = $1->addNext($3); } + ; + +v2kPortSig: onesig { $$=$1; $$->addNext(new AstPort(CRELINE(),V3Parse::s_pinNum++, V3Parse::s_varAttrp->name())); } + ; + +//************************************************ +// Variables + +varRESET: /* empty */ { VARRESET(); } + ; + +varNet: ySUPPLY0 { VARDECL(SUPPLY0); } + | ySUPPLY1 { VARDECL(SUPPLY1); } + | yWIRE { VARDECL(WIRE); } + | yTRI { VARDECL(TRIWIRE); } + ; +varGParam: yPARAM { VARDECL(GPARAM); } + ; +varLParam: yLOCALPARAM { VARDECL(LPARAM); } + ; +varGenVar: yGENVAR { VARDECL(GENVAR); } + ; +varReg: yREG { VARDECL(REG); } + | yINTEGER { VARDECL(INTEGER); } + ; +varInput: yINPUT { VARIO(INPUT); } + ; +varOutput: yOUTPUT { VARIO(OUTPUT); } + ; +varInout: yINOUT { VARIO(INOUT); } + ; + +varSignedE: /*empty*/ { } + | ySIGNED { VARSIGNED(true); } + ; + +v2kNetDeclE: /*empty*/ { } + | varNet { } + ; + +v2kVarDecl: v2kNetDeclE { } + | varReg { } + ; + +v2kPort: varRESET varInput varSignedE v2kNetDeclE regrangeE v2kPortSig { $$ = $6; } + | varRESET varInout varSignedE v2kNetDeclE regrangeE v2kPortSig { $$ = $6; } + | varRESET varOutput varSignedE v2kVarDecl regrangeE v2kPortSig { $$ = $6; } + ; + +ioDecl: varRESET varInput varSignedE v2kNetDeclE regrangeE sigList ';' { $$ = $6; } + | varRESET varInout varSignedE v2kNetDeclE regrangeE sigList ';' { $$ = $6; } + | varRESET varOutput varSignedE v2kVarDecl regrangeE sigList ';' { $$ = $6; } + ; + +varDecl: varRESET varReg varSignedE regrangeE regsigList ';' { $$ = $5; } + | varRESET varGParam varSignedE regrangeE paramList ';' { $$ = $5; } + | varRESET varLParam varSignedE regrangeE paramList ';' { $$ = $5; } + | varRESET varNet varSignedE delayrange netSigList ';' { $$ = $5; } + | varRESET varGenVar varSignedE regsigList ';' { $$ = $4; } + ; + +modParDecl: varRESET varGParam varSignedE regrangeE paramList { $$ = $5; } /* No semicolon*/ + ; + +//************************************************ +// modItemList + +modItemListOrNone: { $$ = NULL; } + | modItemList { $$ = $1; } + ; + +modItemList: modItem { $$ = $1; } + | modItemList modItem { $$ = $1->addNextNull($2); } + ; + +modItem: modOrGenItem { $$ = $1; } + | yGENERATE genItemsBlock yENDGENERATE { $$ = new AstGenerate($1, $2); } + | ySCHDR { $$ = new AstScHdr(CRELINE(),*$1); } + | ySCINT { $$ = new AstScInt(CRELINE(),*$1); } + | ySCIMP { $$ = new AstScImp(CRELINE(),*$1); } + | ySCIMPH { $$ = new AstScImpHdr(CRELINE(),*$1); } + | ySCCTOR { $$ = new AstScCtor(CRELINE(),*$1); } + | yVL_INLINE_MODULE { $$ = new AstPragma($1,AstPragmaType::INLINE_MODULE); } + | yVL_NO_INLINE_MODULE { $$ = new AstPragma($1,AstPragmaType::NO_INLINE_MODULE); } + | yVL_PUBLIC_MODULE { $$ = new AstPragma($1,AstPragmaType::PUBLIC_MODULE); } + | yVL_TRACING_OFF { $$ = NULL; V3Parse::s_trace=false; } + | yVL_TRACING_ON { $$ = NULL; V3Parse::s_trace=v3Global.opt.trace(); } + | ySPECIFY specifyJunkList yENDSPECIFY { $$ = NULL; } + | ySPECIFY yENDSPECIFY { $$ = NULL; } + ; + +modOrGenItem: yALWAYS sensitivityE stmtBlock { $$ = new AstAlways($1,$2,$3); } + | yFINAL stmtBlock { $$ = new AstFinal($1,$2); } + | yINITIAL stmtBlock { $$ = new AstInitial($1,$2); } + | yASSIGN delayE assignList ';' { $$ = $3; } + | yDEFPARAM defpList ';' { $$ = $2; } + | yID instparamListE {INSTPREP(*$1,$2);} instnameList ';' { $$ = $4; V3Parse::s_impliedDecl=false;} + | taskDecl { $$ = $1; } + | funcDecl { $$ = $1; } + | gateDecl { $$ = $1; } + | ioDecl { $$ = $1; } + | varDecl { $$ = $1; } + | pslStmt { $$ = $1; } + ; + +//************************************************ +// genmodItemList + +// Because genItemList includes variable declarations, we don't need beginNamed +genItemBlock: genItem { $$ = new AstBegin(CRELINE(),"genblk",$1); } + | genItemBegin { $$ = $1; } + ; + +genItemsBlock: genItemList { $$ = new AstBegin(CRELINE(),"genblk",$1); } + | genItemBegin { $$ = $1; } + ; + +genItemBegin: yBBEGIN genItemList yBEND { $$ = new AstBegin($1,"genblk",$2); } + | yBBEGIN yBEND { $$ = NULL; } + | yBBEGIN ':' yID genItemList yBEND { $$ = new AstBegin($2,*$3,$4); } + | yBBEGIN ':' yID yBEND { $$ = NULL; } + ; + +genItemList: genItem { $$ = $1; } + | genItemList genItem { $$ = $1->addNextNull($2); } + ; + +genItem: modOrGenItem { $$ = $1; } + | yCASE '(' expr ')' genCaseList yENDCASE { $$ = new AstGenCase($1,$3,$5); } + | yIF expr genItemBlock %prec yLOWER_THAN_ELSE { $$ = new AstGenIf($1,$2,$3,NULL); } + | yIF expr genItemBlock yELSE genItemBlock { $$ = new AstGenIf($1,$2,$3,$5); } + | yFOR '(' lhIdVarRef '=' expr ';' expr ';' lhIdVarRef '=' expr ')' genItemBlock + { $$ = new AstGenFor($1, new AstAssign($4,$3,$5) + ,$7, new AstAssign($10,$9,$11) + ,$13);} + ; + +genCaseList: casecondList ':' genItemBlock { $$ = new AstCaseItem($2,$1,$3); } + | yDEFAULT ':' genItemBlock { $$ = new AstCaseItem($2,NULL,$3); } + | yDEFAULT genItemBlock { $$ = new AstCaseItem($1,NULL,$2); } + | genCaseList casecondList ':' genItemBlock { $$ = $1;$1->addNext(new AstCaseItem($3,$2,$4)); } + | genCaseList yDEFAULT genItemBlock { $$ = $1;$1->addNext(new AstCaseItem($2,NULL,$3)); } + | genCaseList yDEFAULT ':' genItemBlock { $$ = $1;$1->addNext(new AstCaseItem($3,NULL,$4)); } + ; + +//************************************************ +// modItems + +varDeclList: varDecl { $$ = $1; } + | varDecl varDeclList { $$ = $1->addNext($2); } + ; + +assignList: assignOne { $$ = $1; } + | assignList ',' assignOne { $$ = $1->addNext($3); } + ; + +assignOne: lhIdRanged '=' expr { $$ = new AstAssignW($2,$1,$3); } + | '{' lhConcIdList '}' '=' expr { $$ = new AstAssignW($1,$2,$5); } + ; + +delayE: /* empty */ + | '#' dterm {} /* ignored */ + | '#' '(' dterm ')' {} /* ignored */ + | '#' '(' dterm ',' dterm ')' {} /* ignored */ + | '#' '(' dterm ',' dterm ',' dterm ')' {} /* ignored */ + ; + +dterm: yID { $$ = NULL; } + | yINTNUM { $$ = NULL; } + | yFLOATNUM { $$ = NULL; } + ; + +onesig: sigId { $$=$1; } + | sigId sigAttrList { $$=$1; } + ; + +sigId: yID { $$ = V3Parse::createVariable(CRELINE(), *$1, NULL); } + ; + +sigIdRange: yID rangeList { $$ = V3Parse::createVariable(CRELINE(), *$1, $2); } + ; + +regSigId: yID rangeListE { $$ = V3Parse::createVariable(CRELINE(), *$1, $2); } + | yID rangeListE '=' constExpr { $$ = V3Parse::createVariable(CRELINE(), *$1, $2); + $$->addNext(new AstInitial($3,new AstAssign($3, new AstVarRef($3, $$, true), $4))); } + ; + +paramId: yID { $$ = V3Parse::createVariable(CRELINE(), *$1, NULL); } + ; + +sigAttrListE: /*empty*/ {} + | sigAttrList {} + ; + +sigAttrList: sigAttr {} + | sigAttrList sigAttr {} + ; + +sigAttr: yVL_CLOCK { V3Parse::s_varAttrp->attrScClocked(true); } + | yVL_CLOCK_ENABLE { V3Parse::s_varAttrp->attrClockEn(true); } + | yVL_PUBLIC { V3Parse::s_varAttrp->sigPublic(true); } + ; + +sigList: onesig { $$ = $1; } + | sigList ',' onesig { $$ = $1; $1->addNext($3); } + ; + +netSig: sigId sigAttrListE { $$ = $1; } + | sigId sigAttrListE '=' expr { $$ = $1; $1->addNext(new AstAssignW($3,new AstVarRef($3,$1->name(),true),$4)); } + | sigIdRange sigAttrListE { $$ = $1; } + ; + +netSigList: netSig { $$ = $1; } + | netSigList ',' netSig { $$ = $1;$1->addNext($3); } + ; + +regsig: regSigId {} + | regSigId sigAttrList {} + ; + +regsigList: regsig { $$ = $1; } + | regsigList ',' regsig { $$ = $1;$1->addNext($3); } + ; + +rangeListE: /* empty */ { $$ = NULL; } + | rangeList { $$ = $1; } + ; + +rangeList: anyrange { $$ = $1; } + | rangeList anyrange { $$ = $1; $1->addNext($2); } + ; + +regrangeE: /* empty */ { $$ = NULL; VARRANGE($$); } + | anyrange { $$ = $1; VARRANGE($$); } + ; + +anyrange: '[' constExpr ':' constExpr ']' { $$ = new AstRange($1,$2,$4); } + ; + +delayrange: delayE regrangeE { $$ = $2; } + | ySCALARED delayE regrangeE { $$ = $3; } + | yVECTORED delayE regrangeE { $$ = $3; } + ; + +portrangeE: /* empty */ { $$ = NULL; } + | '[' constExpr ']' { $$ = NULL; $1->v3error("Ranges ignored on port-list.\n"); } + | '[' constExpr ':' constExpr ']' { $$ = NULL; $1->v3error("Ranges ignored on port-list.\n"); } + ; + +// Parameters +param: paramId '=' expr { $$ = $1; $$->initp($3); } + | paramId sigAttrList '=' expr { $$ = $1; $$->initp($4); } + ; + +paramList: param { $$ = $1; } + | paramList ',' param { $$ = $1; $1->addNext($3); } + ; + +defpList: defpOne { $$ = $1; } + | defpList ',' defpOne { $$ = $1->addNext($3); } + ; + +defpOne: yID '.' yID '=' expr { $$ = new AstDefParam($4,*$1,*$3,$5); } + ; + +// Instances +instparamListE: /* empty */ { $$ = NULL; } + | '#' '(' cellpinList ')' { $$ = $3; } + ; + +instnameList: instname { $$ = $1; } + | instnameList ',' instname { $$ = $1->addNext($3); } + ; + +instname: yID funcRange '(' cellpinList ')' { $$ = new AstCell($3,*$1,V3Parse::s_instModule,$4,V3Parse::s_instParamp,$2); } + ; + +cellpinList: {V3Parse::s_pinNum=1;} cellpinlist2 { $$ = $2; } + ; + +cellpinlist2: cellpinitemE { $$ = $1; } + | cellpinlist2 ',' cellpinitemE { $$ = $1->addNextNull($3)->castPin(); } + ; + +cellpinitemE: /* empty */ { $$ = NULL; V3Parse::s_pinNum++; } + | '.' yID '(' ')' { $$ = NULL; V3Parse::s_pinNum++; } + | '.' yID '(' expr ')' { $$ = new AstPin($1,V3Parse::s_pinNum++,*$2,$4); } + | expr { $$ = new AstPin(CRELINE(),V3Parse::s_pinNum++,"",$1); } + ; + +sensitivityE: /* empty */ { $$ = NULL; } + | '@' '(' senList ')' { $$ = NEW_SENTREE_BRANCH($1,$3); } + | '@' senitem { $$ = NEW_SENTREE_BRANCH($1,$2); } + | '@' '(' '*' ')' { $$ = NULL; $2->v3error("Use @*. always @ (*) to be depreciated in Verilog 2005.\n"); } + | '@' '*' { $$ = NULL; } /* Verilog 2001 */ + ; + +senList: senitem { $$ = $1; } + | senList yOR senitem { $$ = $1;$1->addNext($3); } + | senList ',' senitem { $$ = $1;$1->addNext($3); } /* Verilog 2001 */ + ; + +senitem: senitemEdge { $$ = $1; } + | idVarRef ignoreRangeE { $$ = new AstSenItem(CRELINE(),AstEdgeType::ANYEDGE,$1); } + ; + +senitemEdge: yPOSEDGE idVarRef ignoreRangeE { $$ = new AstSenItem($1,AstEdgeType::POSEDGE,$2); } + | yNEGEDGE idVarRef ignoreRangeE { $$ = new AstSenItem($1,AstEdgeType::NEGEDGE,$2); } + ; + +ignoreRangeE: /* empty */ { $$ = NULL; } /* ignored */ + | '[' expr ']' { $$ = NULL; } /* ignored */ + | '[' expr ':' expr ']' { $$ = NULL; } /* ignored */ + ; + +stmtBlock: stmt { $$ = $1; } + | yBBEGIN stmtList yBEND { $$ = $2; } + | yBBEGIN yBEND { $$ = NULL; } + | beginNamed stmtList yBEND { $$ = $1; $1->addStmtp($2); } + | beginNamed yBEND { $$ = $1; } + ; + +beginNamed: yBBEGIN ':' yID varDeclList { $$ = new AstBegin($2,*$3,$4); } + | yBBEGIN ':' yID { $$ = new AstBegin($2,*$3,NULL); } + ; + +stmtList: stmtBlock { $$ = $1; } + | stmtList stmtBlock { $$ = ($2==NULL)?($1):($1->addNext($2)); } + ; + +stmt: ';' { $$ = NULL; } + | lhIdRanged yLTE delayE expr ';' { $$ = new AstAssignDly($2,$1,$4); } + | lhIdRanged '=' delayE expr ';' { $$ = new AstAssign($2,$1,$4); } + | lhIdRanged '=' yD_FOPEN '(' expr ',' expr ')' ';' { $$ = new AstFOpen($3,$1,$5,$7); } + | yASSIGN lhIdRanged '=' delayE expr ';' { $$ = new AstAssign($1,$2,$5); } + | '{' lhConcIdList '}' yLTE delayE expr ';' { $$ = new AstAssignDly($4,$2,$6); } + | '{' lhConcIdList '}' '=' delayE expr ';' { $$ = new AstAssign($4,$2,$6); } + | yD_C '(' cStrList ')' ';' { $$ = (v3Global.opt.ignc() ? NULL : new AstUCStmt($1,$3)); } + | yD_FCLOSE '(' idVarXRef ')' ';' { $$ = new AstFClose($1, $3); } + | yD_FINISH ';' { $$ = new AstFinish($1); } + | yD_STOP ';' { $$ = new AstStop($1); } + | yVL_COVER_OFF { $$ = new AstPragma($1,AstPragmaType::COVERAGE_BLOCK_OFF); } + | stateCaseForIf { $$ = $1; } + | taskRef ';' { $$ = $1; } + + | yD_DISPLAY ';' { $$ = new AstDisplay($1,'\n',"",NULL,NULL); } + | yD_DISPLAY '(' ySTRING ')' ';' { $$ = new AstDisplay($1,'\n',*$3,NULL,NULL); } + | yD_DISPLAY '(' ySTRING ',' eList ')' ';' { $$ = new AstDisplay($1,'\n',*$3,NULL,$5); } + | yD_WRITE '(' ySTRING ')' ';' { $$ = new AstDisplay($1,'\0',*$3,NULL,NULL); } + | yD_WRITE '(' ySTRING ',' eList ')' ';' { $$ = new AstDisplay($1,'\0',*$3,NULL,$5); } + | yD_FDISPLAY '(' idVarXRef ',' ySTRING ')' ';' { $$ = new AstDisplay($1,'\n',*$5,$3,NULL); } + | yD_FDISPLAY '(' idVarXRef ',' ySTRING ',' eList ')' ';' { $$ = new AstDisplay($1,'\n',*$5,$3,$7); } + | yD_FWRITE '(' idVarXRef ',' ySTRING ')' ';' { $$ = new AstDisplay($1,'\0',*$5,$3,NULL); } + | yD_FWRITE '(' idVarXRef ',' ySTRING ',' eList ')' ';' { $$ = new AstDisplay($1,'\0',*$5,$3,$7); } + ; + +stateCaseForIf: caseStmt caseAttrE caseList yENDCASE { $$ = $1; $1->addItemsp($3); } + | yIF expr stmtBlock %prec yLOWER_THAN_ELSE { $$ = new AstIf($1,$2,$3,NULL); } + | yIF expr stmtBlock yELSE stmtBlock { $$ = new AstIf($1,$2,$3,$5); } + | yFOR '(' lhIdVarRef '=' expr ';' expr ';' lhIdVarRef '=' expr ')' stmtBlock + { $$ = new AstFor($1, new AstAssign($4,$3,$5) + ,$7, new AstAssign($10,$9,$11) + ,$13);} + ; + +caseStmt: yCASE '(' expr ')' { $$ = V3Parse::s_caseAttrp = new AstCase($1,false,$3,NULL); } + | yCASEX '(' expr ')' { $$ = V3Parse::s_caseAttrp = new AstCase($1,true,$3,NULL); $1->v3warn(CASEX,"Suggest casez (with ?'s) in place of casex (with X's)\n"); } + | yCASEZ '(' expr ')' { $$ = V3Parse::s_caseAttrp = new AstCase($1,true,$3,NULL); } + ; + +caseAttrE: /*empty*/ { } + | caseAttrE yVL_FULL_CASE { V3Parse::s_caseAttrp->fullPragma(true); } + | caseAttrE yVL_PARALLEL_CASE { V3Parse::s_caseAttrp->parallelPragma(true); } + ; + +caseList: casecondList ':' stmtBlock { $$ = new AstCaseItem($2,$1,$3); } + | yDEFAULT ':' stmtBlock { $$ = new AstCaseItem($2,NULL,$3); } + | yDEFAULT stmtBlock { $$ = new AstCaseItem($1,NULL,$2); } + | caseList casecondList ':' stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,$2,$4)); } + | caseList yDEFAULT stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($2,NULL,$3)); } + | caseList yDEFAULT ':' stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,NULL,$4)); } + ; + +casecondList: expr { $$ = $1; } + | casecondList ',' expr { $$ = $1;$1->addNext($3); } + ; + +taskDecl: yTASK yID ';' stmtBlock yENDTASK { $$ = new AstTask ($1,*$2,$4);} + | yTASK yID ';' funcVarList stmtBlock yENDTASK { $$ = new AstTask ($1,*$2,$4); $4->addNextNull($5); } + ; + +funcDecl: yFUNCTION funcRange yID ';' funcVarList stmtBlock yENDFUNCTION { $$ = new AstFunc ($1,*$3,$5,$2); $5->addNextNull($6);} + | yFUNCTION ySIGNED funcRange yID ';' funcVarList stmtBlock yENDFUNCTION { $$ = new AstFunc ($1,*$4,$6,$3); $6->addNextNull($7); $$->isSigned(true); } + ; + +funcRange: '[' constExpr ':' constExpr ']' { $$ = new AstRange($1,$2,$4); } + | { $$ = NULL; } + ; + +funcVarList: funcVar { $$ = $1; } + | funcVarList funcVar { $$ = $1;$1->addNext($2); } + ; + +funcVar: ioDecl { $$ = $1; } + | varDecl { $$ = $1; } + | yVL_PUBLIC { $$ = new AstPragma ($1,AstPragmaType::PUBLIC_TASK); } + ; + +constExpr: expr { $$ = $1; } + ; + +exprNoStr: expr yOROR expr { $$ = new AstLogOr ($2,$1,$3); } + | expr yANDAND expr { $$ = new AstLogAnd ($2,$1,$3); } + | expr '&' expr { $$ = new AstAnd ($2,$1,$3); } + | expr '|' expr { $$ = new AstOr ($2,$1,$3); } + | expr yOP_NAND expr { $$ = new AstNot($2,new AstAnd ($2,$1,$3)); } + | expr yOP_NOR expr { $$ = new AstNot($2,new AstOr ($2,$1,$3)); } + | expr '^' expr { $$ = new AstXor ($2,$1,$3); } + | expr yOP_XNOR expr { $$ = new AstXnor ($2,$1,$3); } + | expr yEQUAL expr { $$ = new AstEq ($2,$1,$3); } + | expr yNOTEQUAL expr { $$ = new AstNeq ($2,$1,$3); } + | expr yCASEEQUAL expr { $$ = new AstEqCase ($2,$1,$3); } + | expr yCASENOTEQUAL expr { $$ = new AstNeqCase ($2,$1,$3); } + | expr '>' expr { $$ = new AstGt ($2,$1,$3); } + | expr '<' expr { $$ = new AstLt ($2,$1,$3); } + | expr yGTE expr { $$ = new AstGte ($2,$1,$3); } + | expr yLTE expr { $$ = new AstLte ($2,$1,$3); } + | expr ySLEFT expr { $$ = new AstShiftL ($2,$1,$3); } + | expr ySRIGHT expr { $$ = new AstShiftR ($2,$1,$3); } + | expr ySSRIGHT expr { $$ = new AstShiftRS ($2,$1,$3); } + | expr '+' expr { $$ = new AstAdd ($2,$1,$3); } + | expr '-' expr { $$ = new AstSub ($2,$1,$3); } + | expr '*' expr { $$ = new AstMul ($2,$1,$3); } + | expr '/' expr { $$ = new AstDiv ($2,$1,$3); } + | expr '%' expr { $$ = new AstModDiv ($2,$1,$3); } + | expr yPOW expr { $$ = new AstPow ($2,$1,$3); } + | expr yOP_LOGIF expr { $$ = new AstLogIf ($2,$1,$3); } + | expr yOP_LOGIFF expr { $$ = new AstLogIff ($2,$1,$3); } + + | '-' expr %prec yUNARYARITH { $$ = new AstUnaryMin ($1,$2); } + | '+' expr %prec yUNARYARITH { $$ = $2; } + | '&' expr %prec yREDUCTION { $$ = new AstRedAnd ($1,$2); } + | '|' expr %prec yREDUCTION { $$ = new AstRedOr ($1,$2); } + | '^' expr %prec yREDUCTION { $$ = new AstRedXor ($1,$2); } + | yOP_XNOR expr %prec yREDUCTION { $$ = new AstRedXnor ($1,$2); } + | yOP_NAND expr %prec yREDUCTION { $$ = new AstNot($1,new AstRedAnd($1,$2)); } + | yOP_NOR expr %prec yREDUCTION { $$ = new AstNot($1,new AstRedOr ($1,$2)); } + | '!' expr %prec yNEGATION { $$ = new AstLogNot ($1,$2); } + | '~' expr %prec yNEGATION { $$ = new AstNot ($1,$2); } + + | expr '?' expr ':' expr { $$ = new AstCond($2,$1,$3,$5); } + | '(' expr ')' { $$ = $2; } + | '_' '(' statePushVlg expr statePop ')' { $$ = $4; } // Arbitrary Verilog inside PSL + | '{' cateList '}' { $$ = $2; } + | '{' constExpr '{' cateList '}' '}' { $$ = new AstReplicate($1,$4,$2); } + + | yD_BITS '(' expr ')' { $$ = new AstAttrOf($1,AstAttrType::BITS,$3); } + | yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? NULL : new AstUCFunc($1,$3)); } + | yD_COUNTONES '(' expr ')' { $$ = new AstCountOnes($1,$3); } + | yD_ISUNKNOWN '(' expr ')' { $$ = new AstIsUnknown($1,$3); } + | yD_ONEHOT '(' expr ')' { $$ = new AstOneHot($1,$3); } + | yD_ONEHOT0 '(' expr ')' { $$ = new AstOneHot0($1,$3); } + | yD_SIGNED '(' expr ')' { $$ = new AstSigned($1,$3); } + | yD_TIME { $$ = new AstTime($1); } + | yD_UNSIGNED '(' expr ')' { $$ = new AstUnsigned($1,$3); } + + | yID '(' eList ')' { $$ = new AstFuncRef($2,*$1,"",$3); } + | pathDotted '.' yID '(' eList ')' { $$ = new AstFuncRef($4,*$3,*$1,$5); } + + | yINTNUM { $$ = new AstConst(CRELINE(),*$1); } + + | idRanged { $$ = $1; } + ; + +// Generic expressions +expr: exprNoStr { $$ = $1; } + | strAsInt { $$ = $1; } + ; + +// Psl excludes {}'s by lexer converting to different token +exprPsl: exprNoStr { $$ = $1; } + | strAsInt { $$ = $1; } + ; + +// PLI calls exclude "" as integers, they're strings +// For $c("foo","bar") we want "bar" as a string, not a Verilog integer. +exprStrText: exprNoStr { $$ = $1; } + | strAsText { $$ = $1; } + ; + +cStrList: exprStrText { $$ = $1; } + | exprStrText ',' cStrList { $$ = $1;$1->addNext($3); } + ; + +cateList: expr { $$ = $1; } + | cateList ',' expr { $$ = new AstConcat($2,$1,$3); } + ; + +eList: expr { $$ = $1; } + | eList ',' expr { $$ = $1;$1->addNext($3); } + ; + +// Gate declarations +gateDecl: yBUF gateBufList ';' { $$ = $2; } + | yNOT gateNotList ';' { $$ = $2; } + | yAND gateAndList ';' { $$ = $2; } + | yNAND gateNandList ';' { $$ = $2; } + | yOR gateOrList ';' { $$ = $2; } + | yNOR gateNorList ';' { $$ = $2; } + | yXOR gateXorList ';' { $$ = $2; } + | yXNOR gateXnorList ';' { $$ = $2; } + ; + +gateBufList: gateBuf { $$ = $1; } + | gateBuf ',' gateBuf { $$ = $1->addNext($3); } + ; +gateNotList: gateNot { $$ = $1; } + | gateNot ',' gateNot { $$ = $1->addNext($3); } + ; +gateAndList: gateAnd { $$ = $1; } + | gateAnd ',' gateAnd { $$ = $1->addNext($3); } + ; +gateNandList: gateNand { $$ = $1; } + | gateNand ',' gateNand { $$ = $1->addNext($3); } + ; +gateOrList: gateOr { $$ = $1; } + | gateOr ',' gateOr { $$ = $1->addNext($3); } + ; +gateNorList: gateNor { $$ = $1; } + | gateNor ',' gateNor { $$ = $1->addNext($3); } + ; +gateXorList: gateXor { $$ = $1; } + | gateXor ',' gateXor { $$ = $1->addNext($3); } + ; +gateXnorList: gateXnor { $$ = $1; } + | gateXnor ',' gateXnor { $$ = $1->addNext($3); } + ; + +gateIdE: /*empty*/ {} + | yID {} + ; + +gateBuf: gateIdE '(' lhIdRanged ',' expr ')' { $$ = new AstAssignW ($2,$3,$5); $$->allowImplicit(true); } + ; +gateNot: gateIdE '(' lhIdRanged ',' expr ')' { $$ = new AstAssignW ($2,$3,new AstNot($4,$5)); $$->allowImplicit(true); } + ; +gateAnd: gateIdE '(' lhIdRanged ',' gateAndPinList ')' { $$ = new AstAssignW ($2,$3,$5); $$->allowImplicit(true); } + ; +gateNand: gateIdE '(' lhIdRanged ',' gateAndPinList ')' { $$ = new AstAssignW ($2,$3,new AstNot($4,$5)); $$->allowImplicit(true); } + ; +gateOr: gateIdE '(' lhIdRanged ',' gateOrPinList ')' { $$ = new AstAssignW ($2,$3,$5); $$->allowImplicit(true); } + ; +gateNor: gateIdE '(' lhIdRanged ',' gateOrPinList ')' { $$ = new AstAssignW ($2,$3,new AstNot($4,$5)); $$->allowImplicit(true); } + ; +gateXor: gateIdE '(' lhIdRanged ',' gateXorPinList ')' { $$ = new AstAssignW ($2,$3,$5); $$->allowImplicit(true); } + ; +gateXnor: gateIdE '(' lhIdRanged ',' gateXorPinList ')' { $$ = new AstAssignW ($2,$3,new AstNot($4,$5)); $$->allowImplicit(true); } + ; + +gateAndPinList: expr { $$ = $1; } + | gateAndPinList ',' expr { $$ = new AstAnd($2,$1,$3); } + ; +gateOrPinList: expr { $$ = $1; } + | gateOrPinList ',' expr { $$ = new AstOr($2,$1,$3); } + ; +gateXorPinList: expr { $$ = $1; } + | gateXorPinList ',' expr { $$ = new AstXor($2,$1,$3); } + ; + +//************************************************ +// Specify +specifyJunkList: specifyJunk /* ignored */ + | specifyJunkList specifyJunk /* ignored */ + ; + +specifyJunk: dterm {} /* ignored */ + | ySTRING {} + | ';' {} + | '!' {} + | '&' {} + | '(' {} + | ')' {} + | '*' {} | '/' {} | '%' {} | yPOW {} + | '+' {} | '-' {} + | ',' {} + | ':' {} + | '$' {} + | '=' {} + | '>' {} | '<' {} + | '?' {} + | '^' {} + | '{' {} | '}' {} + | '[' {} | ']' {} + | '|' {} + | '~' {} + | yANDAND {} | yGTE {} | yLTE {} + | yEQUAL {} | yNOTEQUAL {} + | yIF {} + | yNEGATION {} + | yNEGEDGE {} + | yOP_XNOR {} | yOP_NOR {} | yOP_NAND {} + | yOROR {} + | yPOSEDGE {} + | yREDUCTION {} + | ySLEFT {} | ySRIGHT {} | ySSRIGHT {} + | yPLUSCOLON {} | yMINUSCOLON {} + | yUNARYARITH {} + | yTIMINGSPEC {} + + | yOP_LOGIF {} + | yOP_LOGIFF {} + | yBRA_STAR {} + | yBRA_STAR_KET {} + | yBRA_PLUS_KET {} + | yBRA_MINUS_GT {} + | yBRA_MINUS_GT_KET {} + | yBRA_EQ {} + | yPSL_BRA {} + | yPSL_KET {} + | yOR_MINUS_GT {} + | yOR_EQ_GT {} + ; + +//************************************************ +// IDs +pathDotted: yID { $$ = $1; } + | pathDotted '.' yID { $$ = V3Read::newString(*$1+string(".")+*$3); } + ; + +lhIdVarRef: yID { $$ = new AstVarRef(CRELINE(),*$1,true);} + ; + +lhIdVarXRef: lhIdVarRef { $$ = $1; } + | pathDotted '.' yID { $$ = new AstVarXRef(CRELINE(),*$3,*$1,true);} + ; + +idVarRef: yID { $$ = new AstVarRef(CRELINE(),*$1,false);} + ; + +idVarXRef: idVarRef { $$ = $1; } + | pathDotted '.' yID { $$ = new AstVarXRef(CRELINE(),*$3,*$1,false);} + ; + +taskRef: yID { $$ = new AstTaskRef(CRELINE(),*$1,"",NULL);} + | yID '(' eList ')' { $$ = new AstTaskRef(CRELINE(),*$1,"",$3);} + | pathDotted '.' yID { $$ = new AstTaskRef(CRELINE(),*$3,*$1,NULL);} + | pathDotted '.' yID '(' eList ')' { $$ = new AstTaskRef(CRELINE(),*$3,*$1,$5);} + ; + +idArrayed: idVarXRef { $$ = $1; } + | idArrayed '[' expr ']' { $$ = new AstSelBit($2,$1,$3); } // Or AstArraySel, don't know yet. + ; + +lhIdArrayed: lhIdVarXRef { $$ = $1; } + | lhIdArrayed '[' expr ']' { $$ = new AstSelBit($2,$1,$3); } // Or AstArraySel, don't know yet. + ; + +idRanged: idArrayed { $$ = $1; } + | idArrayed '[' constExpr ':' constExpr ']' { $$ = new AstSelExtract($2,$1,$3,$5); } + | idArrayed '[' expr yPLUSCOLON constExpr ']' { $$ = new AstSelPlus($2,$1,$3,$5); } + | idArrayed '[' expr yMINUSCOLON constExpr ']' { $$ = new AstSelMinus($2,$1,$3,$5); } + ; + +lhIdRanged: lhIdArrayed { $$ = $1; } + | lhIdArrayed '[' constExpr ':' constExpr ']' { $$ = new AstSelExtract($2,$1,$3,$5); } + | lhIdArrayed '[' expr yPLUSCOLON constExpr ']' { $$ = new AstSelPlus($2,$1,$3,$5); } + | lhIdArrayed '[' expr yMINUSCOLON constExpr ']' { $$ = new AstSelMinus($2,$1,$3,$5); } + ; + +strAsInt: ySTRING { $$ = new AstConst(CRELINE(),V3Number(V3Number::VerilogString(),CRELINE(),V3Parse::deQuote(CRELINE(),*$1)));} + ; + +strAsText: ySTRING { $$ = V3Parse::createTextQuoted(CRELINE(),*$1);} + ; + +lhConcIdList: lhIdRanged { $$ = $1; } + | lhConcIdList ',' lhIdRanged { $$ = new AstConcat($2,$1,$3); } + ; + +//************************************************ +// PSL Statements + +pslStmt: yPSL pslDir stateExitPsl { $$ = $2; } + | yPSL pslDecl stateExitPsl { $$ = $2; } + ; + +pslDir: yID ':' pslDirOne { $$ = $3; } // ADD: Create label on $1 + | pslDirOne { $$ = $1; } + ; + +//ADD: | yRESTRICT pslSequence ';' { $$ = PSLUNSUP(new AstPslRestrict($1,$2)); } +pslDirOne: yASSERT pslProp ';' { $$ = new AstPslAssert($1,$2); } + | yASSERT pslProp yREPORT ySTRING ';' { $$ = new AstPslAssert($1,$2,*$4); } + | yCOVER pslProp ';' { $$ = new AstPslCover($1,$2); } + | yCOVER pslProp yREPORT ySTRING ';' { $$ = new AstPslCover($1,$2,*$4); } + ; + +pslDecl: yDEFAULT yCLOCK '=' senitemEdge ';' { $$ = new AstPslDefClock($3, $4); } + | yDEFAULT yCLOCK '=' '(' senitemEdge ')' ';' { $$ = new AstPslDefClock($3, $5); } + ; + +//************************************************ +// PSL Properties, Sequences and SEREs +// Don't use '{' or '}'; in PSL they're yPSL_BRA and yPSL_KET to avoid expr concatenates + +pslProp: pslSequence { $$ = $1; } + | pslSequence '@' %prec prPSLCLK '(' senitemEdge ')' { $$ = new AstPslClocked($2, $4, $1); } // or pslSequence @ ...? + ; + +//ADD: | pslCallSeq { $$ = PSLUNSUP($1); } +//ADD: | pslRepeat { $$ = PSLUNSUP($1); } +pslSequence: yPSL_BRA pslSere yPSL_KET { $$ = $2; } + ; + +//ADD: | pslSere ';' pslSere %prec prPSLCONC { $$ = PSLUNSUP(new AstPslSeqConcat($2, $1, $3)); } +//ADD: | pslSere ':' pslSere %prec prPSLFUS { $$ = PSLUNSUP(new AstPslSeqFusion($2, $1, $3)); } +//ADD: | pslSereCpnd { $$ = $1; } +pslSere: pslExpr { $$ = $1; } + | pslSequence { $$ = $1; } // Sequence containing sequence + ; + +// Undocumented PSL rule is that {} is always a SERE; concatenation is not allowed. +// This can be bypassed with the _(...) embedding of any arbitrary expression. +//ADD: | pslFunc { $$ = UNSUP($1); } +//ADD: | pslExpr yUNION pslExpr { $$ = UNSUP(new AstPslUnion($2, $1, $3)); } +pslExpr: exprPsl { $$ = new AstPslBool($1->fileline(), $1); } + | yTRUE { $$ = new AstPslBool($1, new AstConst($1, V3Number($1,1,1))); } + ; + +//********************************************************************** +//********************************************************************** +//********************************************************************** +%% + +AstNode* V3Parse::createSupplyExpr(FileLine* fileline, string name, int value) { + FileLine* newfl = new FileLine (fileline); + newfl->warnOff(V3ErrorCode::WIDTH, true); + AstNode* nodep = new AstConst(newfl, V3Number(fileline)); + // Adding a NOT is less work then figuring out how wide to make it + if (value) nodep = new AstNot(newfl, nodep); + nodep = new AstAssignW(newfl, new AstVarRef(fileline, name, true), + nodep); + return nodep; +} + +AstVar* V3Parse::createVariable(FileLine* fileline, string name, AstRange* arrayp) { + AstVarType type = V3Parse::s_varIO; + AstRange* rangep = V3Parse::s_varRangep; + if (type == AstVarType::UNKNOWN) type = V3Parse::s_varDecl; + if (type == AstVarType::UNKNOWN) fileline->v3fatalSrc("Unknown signal type declared"); + // Linting, because we allow parsing of a superset of the language + if (type == AstVarType::INTEGER || type == AstVarType::GENVAR) { + if (rangep) fileline->v3error("Integers may not be ranged: "<v3error("Genvars may not be arrayed: "<cloneTree(false), + arrayp); + nodep->isSigned(V3Parse::s_varSigned); +#ifndef VL_UNSIGNED + if (type == AstVarType::INTEGER) nodep->isSigned(true); +#endif + if (V3Parse::s_varDecl != AstVarType::UNKNOWN) nodep->combineType(V3Parse::s_varDecl); + if (V3Parse::s_varIO != AstVarType::UNKNOWN) nodep->combineType(V3Parse::s_varIO); + + if (V3Parse::s_varDecl == AstVarType::SUPPLY0) { + nodep->addNext(V3Parse::createSupplyExpr(fileline, nodep->name(), 0)); + } + if (V3Parse::s_varDecl == AstVarType::SUPPLY1) { + nodep->addNext(V3Parse::createSupplyExpr(fileline, nodep->name(), 1)); + } + // Clear any widths that got presumed by the ranging; + // We need to autosize parameters and integers separately + nodep->width(0,0); + // Propagate from current module tracing state + if (nodep->isGenVar() || nodep->isParam()) nodep->trace(false); + else nodep->trace(V3Parse::s_trace); + + // Remember the last variable created, so we can attach attributes to it in later parsing + V3Parse::s_varAttrp = nodep; + return nodep; +} + +string V3Parse::deQuote(FileLine* fileline, string text) { + // Fix up the quoted strings the user put in, for example "\"" becomes " + bool quoted = false; + string newtext; + unsigned char octal_val = 0; + int octal_digits = 0; + for (const char* cp=text.c_str(); *cp; ++cp) { + if (quoted) { + if (isdigit(*cp)) { + octal_val = octal_val*8 + (*cp-'0'); + if (++octal_digits == 3) { + octal_digits = 0; + quoted = false; + newtext += octal_val; + } + } else { + if (octal_digits) { + fileline->v3error("Non-three digit octal escape code (\\###)"); + octal_digits = 0; + } + quoted = false; + if (*cp == 'n') newtext += '\n'; + else if (*cp == 'a') newtext += '\a'; // SystemVerilog 3.1 + else if (*cp == 'f') newtext += '\f'; // SystemVerilog 3.1 + else if (*cp == 'r') newtext += '\r'; + else if (*cp == 't') newtext += '\t'; + else if (*cp == 'v') newtext += '\v'; // SystemVerilog 3.1 + else if (*cp == 'x' && isxdigit(cp[1]) && isxdigit(cp[2])) { // SystemVerilog 3.1 +#define vl_decodexdigit(c) ((isdigit(c)?((c)-'0'):(tolower((c))-'a'+10))) + newtext += (char)(16*vl_decodexdigit(cp[1]) + vl_decodexdigit(cp[2])); + cp += 2; + } + else if (isalnum(*cp)) { + fileline->v3error("Unknown escape sequence: \\"<<*cp); + break; + } + else newtext += *cp; + } + } + else if (*cp == '\\') { + quoted = true; + octal_digits = 0; + } + else if (*cp != '"') { + newtext += *cp; + } + } + return newtext; +} + +AstText* V3Parse::createTextQuoted(FileLine* fileline, string text) { + string newtext = deQuote(fileline, text); + return new AstText(fileline, newtext); +} diff --git a/test_c/.cvsignore b/test_c/.cvsignore new file mode 100644 index 000000000..2f695bf80 --- /dev/null +++ b/test_c/.cvsignore @@ -0,0 +1,6 @@ +*.dmp +*.log +*.csrc +*.vcd +obj_* +project diff --git a/test_c/Makefile b/test_c/Makefile new file mode 100644 index 000000000..24aa54073 --- /dev/null +++ b/test_c/Makefile @@ -0,0 +1,55 @@ +# $Id$ +#***************************************************************************** +# +# DESCRIPTION: Verilator Example: Makefile for inside source directory +# +# This calls the object directory makefile. That allows the objects to +# be placed in the "current directory" which simplifies the Makefile. +# +# 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 must point to the root of the VERILATOR kit +VERILATOR_ROOT := $(shell pwd)/.. +export VERILATOR_ROOT +PWD := $(shell pwd) + +DEBUG_ON = --debug --trace-dups +#DEBUG = $(DEBUG_ON) +VALGRIND_ON = $(DEBUG_ON) --gdb "valgrind -v --leak-check=yes" + +###################################################################### +default: prep compile run +debug: prep_dbg compile run +valgrind: prep_vg compile run + +VERILATOR_FLAGS = --cc -f $(PWD)/../test_v/input.vc top.v +#Note turning on traces requires SystemPerl to be installed +#VERILATOR_FLAGS += --trace + +prep: + perl $(VERILATOR_ROOT)/bin/verilator $(DEBUG) $(VERILATOR_FLAGS) +prep_dbg: + perl $(VERILATOR_ROOT)/bin/verilator $(DEBUG_ON) $(VERILATOR_FLAGS) +prep_vg: + perl $(VERILATOR_ROOT)/bin/verilator $(VALGRIND_ON) $(VERILATOR_FLAGS) + +compile: + cd obj_dir ; $(MAKE) -j 3 -f ../Makefile_obj + +run: + obj_dir/simx + +###################################################################### + +obj_dir: + mkdir $@ + +###################################################################### + +maintainer-copy:: +clean mostlyclean distclean maintainer-clean:: + -rm -rf obj_dir *.log *.dmp *.vpd core diff --git a/test_c/Makefile_obj b/test_c/Makefile_obj new file mode 100644 index 000000000..0bfd48f6a --- /dev/null +++ b/test_c/Makefile_obj @@ -0,0 +1,39 @@ +# $Id$ -*- Makefile -*- +#***************************************************************************** +# +# DESCRIPTION: Verilator Example: Makefile for inside object directory +# +# This executed in the object directory, and called by ../Makefile +# +# 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. +# +#***************************************************************************** + +default: simx +include Vtop.mk + +####################################################################### +# Compile flags + +CPPFLAGS += -DVL_DEBUG=1 +ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users +CPPFLAGS += -DVL_THREADED=1 +CPPFLAGS += -W +CPPFLAGS += -Werror +#CPPFLAGS += -pedantic-errors +endif + +####################################################################### +# Linking final exe -- presumes have a sim_main.cpp + +SP_SRCS = verilated.o +ifeq ($(VM_TRACE),1) + SP_SRCS += SpTraceVcdC.o +endif + +simx: sim_main.o $(SP_SRCS) $(VM_PREFIX)__ALL.a + $(CXX) $(LDFLAGS) -g $^ $(LOADLIBES) $(LDLIBS) -o $@ $(LIBS) 2>&1 | c++filt + +sim_main.o: sim_main.cpp $(VM_PREFIX).h diff --git a/test_c/sim_main.cpp b/test_c/sim_main.cpp new file mode 100644 index 000000000..f143d0733 --- /dev/null +++ b/test_c/sim_main.cpp @@ -0,0 +1,83 @@ +// $Id$ +// DESCRIPTION: Verilator Example: Top level main for invoking model +// +// 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. + +#include // Defines common routines +#include "Vtop.h" // From Verilating "top.v" +#if VM_TRACE +# include // Trace file format header (from SystemPerl) +#endif + +Vtop *top; // Instantiation of module + +unsigned int main_time = 0; // Current simulation time + +double sc_time_stamp () { // Called by $time in Verilog + return main_time; +} + +int main(int argc, char **argv, char **env) { + if (0 && argc && argv && env) {} // Prevent unused variable warnings + top = new Vtop; // Create instance of module + + Verilated::debug(0); + +#if VM_TRACE // If verilator was invoked with --trace + Verilated::traceEverOn(true); // Verilator must compute traced signals + cout << "Enabling waves...\n"; + SpTraceVcdCFile* tfp = new SpTraceVcdCFile; + top->trace (tfp, 99); // Trace 99 levels of hierarchy + tfp->open ("vl_dump.vcd"); // Open the dump file +#endif + + top->reset_l = 1; // Set some inputs + top->fastclk = 0; + top->clk = 0; + top->passed = 0; + + while (main_time < 60 && !top->passed) { + + if ((main_time % 10) == 3) { // Toggle clock + top->clk = 1; + } + if ((main_time % 10) == 8) { + top->clk = 0; + } + if (main_time > 10) { + top->reset_l = 1; // Deassert reset + } else if (main_time > 1) { + top->reset_l = 0; // Assert reset + } + + top->eval(); // Evaluate model +#if VM_TRACE + tfp->dump (main_time); // Create waveform trace for this timestamp +#endif + + // Read outputs + VL_PRINTF ("[%d] %x %x %x %x %x_%08x_%08x\n", + main_time, top->clk, top->reset_l, top->passed, + top->out_small, top->out_wide[2], top->out_wide[1], top->out_wide[0]); + + top->fastclk = !top->fastclk; + main_time++; // Time passes... + } + + top->final(); + +#if VM_TRACE + tfp->close(); +#endif + + if (!top->passed) { + VL_PRINTF ("A Test failed\n"); + abort(); + } else { + VL_PRINTF ("All Tests passed\n"); + } + + exit(0L); +} diff --git a/test_regress/.cvsignore b/test_regress/.cvsignore new file mode 100644 index 000000000..2068b0f0c --- /dev/null +++ b/test_regress/.cvsignore @@ -0,0 +1,10 @@ +*.old +obj_dir +vcs.key +csrc +simv* +simx* +*.log +ncverilog.* +INCA_libs +logs diff --git a/test_regress/Makefile b/test_regress/Makefile new file mode 100644 index 000000000..f5e0d81e5 --- /dev/null +++ b/test_regress/Makefile @@ -0,0 +1,73 @@ +# $Id$ */ +#***************************************************************************** +# +# DESCRIPTION: Verilator Example: Makefile for inside source directory +# +# This calls the object directory makefile. That allows the objects to +# be placed in the "current directory" which simplifies the Makefile. +# +# Copyright 2003-2005 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 must point to the root of the VERILATOR kit +VERILATOR_ROOT := $(shell pwd)/.. +export VERILATOR_ROOT +PERL = perl +PWD := $(shell pwd) + +###################################################################### + +default: test + +ifneq ($(VCS_HOME),) +PRODUCTS += --vcs +endif + +ifneq ($(NC_ROOT),) +PRODUCTS += --nc +endif + +PRODUCTS += --v3 + +ifeq ($(VERILATOR_AUTHOR_SITE),1) + DRIVER_FLAGS += -j 4 +endif + +###################################################################### + +test: + $(PERL) driver.pl $(DRIVER_FLAGS) $(PRODUCTS) + +###################################################################### + +vcs: + $(PERL) driver.pl $(DRIVER_FLAGS) --vcs --stop + +###################################################################### + +nc: + $(PERL) driver.pl $(DRIVER_FLAGS) --nc --stop + +###################################################################### + +v3: + $(PERL) driver.pl $(DRIVER_FLAGS) --v3 --stop + +###################################################################### + +random: + $(PERL) driver.pl $(DRIVER_FLAGS) --optimize : --stop + +random_forever: + while ( VERILATOR_NO_DEBUG=1 CPPFLAGS_ADD=-Wno-error $(MAKE) random ) ; do \ + echo ; \ + done + +###################################################################### +maintainer-copy:: +clean mostlyclean distclean maintainer-clean:: + -rm -rf obj_dir simv* simx* csrc INCA_libs *.log logs + diff --git a/test_regress/Makefile_obj b/test_regress/Makefile_obj new file mode 100644 index 000000000..c465e0511 --- /dev/null +++ b/test_regress/Makefile_obj @@ -0,0 +1,58 @@ +# $Id$ -*- Makefile -*- +#***************************************************************************** +# +# DESCRIPTION: Verilator Example: Makefile for inside object directory +# +# This executed in the object directory, and called by ../Makefile +# +# Copyright 2003-2005 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. +# +#***************************************************************************** + +default: $(VM_PREFIX) + +ifneq ($(MAKE_MAIN),0) + # Add main to classes rather then SP_SRCS to save a compiler run + VM_CLASSES += ${VM_PREFIX}__main +endif + +include $(VM_PREFIX).mk + +####################################################################### + +ifeq ($(VERILATOR_AUTHOR_SITE),1) +#OBJCACHE := objcache --read --write +#OBJCACHE_HOSTS := +endif + +####################################################################### + +# Needed by tracing routines +CPPFLAGS += -DVL_DEBUG=1 +CPPFLAGS += -DVM_PREFIX=$(VM_PREFIX) +CPPFLAGS += $(CPPFLAGS_ADD) + +####################################################################### +# Linking final exe + +ifeq ($(VM_SP_OR_SC),1) + LIBS += $(SC_LIBS) +endif + +#Default compile, using normal make rules +#$(VM_PREFIX): $(SP_SRCS) $(VK_OBJS) +# $(CXX) $(LDFLAGS) -g $^ $(LOADLIBES) $(LDLIBS) -o $@ $(LIBS) 2>&1 | c++filt + +#Our own compile rules; Faster compile, all in one file +$(VM_PREFIX)__ALLboth.cpp: $(VK_CLASSES_CPP) $(VK_SUPPORT_CPP) + $(SP_INCLUDER) $^ > $@ + +$(VM_PREFIX)__ALLboth.o: $(VM_PREFIX)__ALLboth.cpp + $(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $< + +ifneq ($(MAKE_MAIN),0) + $(VM_PREFIX): $(VM_PREFIX)__ALLboth.o + $(CXX) $(LDFLAGS) -g $^ $(LOADLIBES) $(LDLIBS) -o $@ $(LIBS) 2>&1 | c++filt +endif diff --git a/test_regress/driver.pl b/test_regress/driver.pl new file mode 100755 index 000000000..922b7249d --- /dev/null +++ b/test_regress/driver.pl @@ -0,0 +1,832 @@ +#!/usr/bin/perl -w +# $Id$ +###################################################################### +# +# This program is Copyright 2003-2005 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 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; +BEGIN { + if (my $Project=($ENV{DIRPROJECT}||$ENV{PROJECT})) { + # Magic to allow author testing of perl packages in local directory + require "$Project/hw/utils/perltools/boot.pl"; + } +} + +use Getopt::Long; +use IO::File; +use Pod::Usage; +use Data::Dumper; +use strict; +use vars qw ($Debug %Vars $Driver $Fork); + +$::Driver = 1; + +eval "use Parallel::Forker; \$Fork=Parallel::Forker->new();"; +$Fork = Forker->new() if !$Fork; +$SIG{CHLD} = sub { $Fork->sig_child() if $Fork; }; +$SIG{TERM} = sub { $Fork->kill_tree_all('TERM') if $Fork; die "Quitting...\n"; }; + +#====================================================================== + +#====================================================================== +# main + +autoflush STDOUT 1; +autoflush STDERR 1; + +our @Orig_ARGV = @ARGV; +our @Orig_ARGV_Sw; foreach (@Orig_ARGV) { push @Orig_ARGV_Sw, $_ if /^-/ && !/^-j/; } + +$Debug = 0; +my @opt_tests; +my $opt_nc; +my $opt_vcs; +my $opt_v3; +my $opt_stop; +my $opt_optimize; +my $opt_gdb; +my $opt_jobs = 1; +my $Opt_Verilated_Debug; +if (! GetOptions ( + "help" => \&usage, + "debug" => \&debug, + "vcs!" => \$opt_vcs, + "verilated_debug!" => \$Opt_Verilated_Debug, + "j=i" => \$opt_jobs, + "v3!" => \$opt_v3, + "nc!" => \$opt_nc, + "gdb!" => \$opt_gdb, + "stop!" => \$opt_stop, + "optimize:s" => \$opt_optimize, + "<>" => \¶meter, + )) { + usage(); +} +$Fork->max_proc($opt_jobs); + +if (!$opt_vcs && !$opt_nc && !$opt_v3) { + $opt_v3 = 1; +} + +if ($#opt_tests<0) { + push @opt_tests, glob ("t/*.pl"); +} + +mkdir "obj_dir"; +mkdir "logs"; + +my $okcnt=0; my $failcnt=0; +my @fails; + +foreach my $testpl (@opt_tests) { + one_test(pl_filename => $testpl, vcs=>1) if $opt_vcs; + one_test(pl_filename => $testpl, nc=>1) if $opt_nc; + one_test(pl_filename => $testpl, 'v3'=>1) if $opt_v3; +} + +$Fork->wait_all(); # Wait for all children to finish + +sub one_test { + my @params = @_; + $Fork->schedule + ( + run_on_start => sub { + print ("="x70,"\n"); + my $test = new VTest(@params); + $test->oprint("="x50,"\n"); + unlink $test->{status_filename}; + $test->read; + if ($test->ok) { + $test->oprint("Test PASSED\n"); + } else { + $test->error("Missing ok\n") if !$test->errors; + $test->oprint("%Error: $test->{errors}\n"); + } + $test->write_status; + }, + run_on_finish => sub { + my $test = new VTest(@params); + $test->read_status; + if ($test->ok) { + $okcnt++; + } else { + $test->oprint("FAILED: ","*"x60,"\n"); + push @fails, "\t#".$test->soprint("%Error: $test->{errors}\n"); + push @fails, "\t\tmake && ( cd test_regress ; " + .$test->{pl_filename}." ".join(' ',@Orig_ARGV_Sw)." )\n"; + $failcnt++; + if ($opt_stop) { die "%Error: --stop and errors found\n"; } + } + }, + )->ready(); +} + +print "\n"; +print "="x70,"\n"; +print "TESTS Passed $okcnt Failed $failcnt\n"; +foreach my $f (@fails) { + chomp $f; + print "$f\n"; +} +print "TESTS Passed $okcnt Failed $failcnt\n"; +exit(10) if $failcnt; + +#---------------------------------------------------------------------- + +sub usage { + print '$Id$ ', "\n"; + pod2usage(-verbose=>2, -exitval => 2); + exit (1); +} + +sub debug { + $Debug = 1; +} + +sub parameter { + my $param = shift; + if ($param =~ /\.pl/) { + push @opt_tests, $param; + } else { + die "%Error: Unknown parameter: $param\n"; + } +} + +####################################################################### +####################################################################### +####################################################################### +####################################################################### +# Test class + +package VTest; +use Data::Dumper; +use Carp; +use Cwd; + +use vars qw ($Last_Self); +use strict; + +sub new { + my $class = shift; + my $self = { + name => undef, # Set below, name of this test + mode => "", + pl_filename => undef, # Name of .pl file to get setup from + make_top_shell => 1, # Make a default __top.v file + make_main => 1, # Make __main.cpp + # All compilers + v_flags => [split(/\s+/," -f input.vc")], + v_flags2 => [], # Overridden in some sim files + v_other_filenames => [], # After the filename so we can spec multiple files + # VCS + vcs => 0, + vcs_flags => [split(/\s+/,"+cli -I +define+vcs+1 -q +v2k")], + # NC + nc => 0, + nc_flags => [split(/\s+/,"+licqueue +nowarn+LIBNOU +define+nc=1 -q +assert +sv31a")], + # Verilator + 'v3' => 0, + verilator_flags => [split(/\s+/,"-cc")], + verilator_make_gcc => 1, + stdout_filename => undef, # Redirect stdout + @_}; + bless $self, $class; + + $self->{name} ||= $1 if $self->{pl_filename} =~ m!.*/([^/]*)\.pl$!; + $self->{mode} ||= "vcs" if $self->{vcs}; + $self->{mode} ||= "v3" if $self->{v3}; + $self->{mode} ||= "nc" if $self->{nc}; + $self->{VM_PREFIX} ||= "V".$self->{name}; + $self->{stats} ||= "obj_dir/V".$self->{name}."__stats.txt"; + $self->{status_filename} ||= "obj_dir/V".$self->{name}.".status"; + $self->{coverage_filename} ||= "obj_dir/V".$self->{name}."_coverage.pl"; + ($self->{top_filename} = $self->{pl_filename}) =~ s/\.pl$/\.v/; + if (!$self->{make_top_shell}) { + $self->{top_shell_filename} = $self->{top_filename}; + } else { + $self->{top_shell_filename} = "obj_dir/$self->{VM_PREFIX}__top.v"; + } + return $self; +} + +sub soprint { + my $self = shift; + my $str = "$self->{mode}/$self->{name}: ".join('',@_); + $str =~ s/\n\n+$/\n/s; + return $str; +} + +sub oprint { + my $self = shift; + print $self->soprint(@_); +} + +sub error { + my $self = shift; + my $msg = join('',@_); + warn "%Warning: $self->{mode}/$self->{name}: ".$msg."\n"; + $self->{errors} ||= $msg; +} + +sub read { + my $self = shift; + # Read the control file + (-r $self->{pl_filename}) + or return $self->error("Can't open $self->{pl_filename}\n"); + $Last_Self = $self; + delete $INC{$self->{pl_filename}}; + require $self->{pl_filename}; +} + +sub write_status { + my $self = shift; + my $filename = $self->{status_filename}; + my $fh = IO::File->new($filename,"w") or die "%Error: $! $filename,"; + print $fh Dumper($self); + print $fh "1;"; + $fh->close(); +} + +sub read_status { + my $self = shift; + my $filename = $self->{status_filename}; + use vars qw($VAR1); + local $VAR1; + require $filename or die "%Error: $! $filename,"; + %{$self} = %{$VAR1}; +} + +#---------------------------------------------------------------------- +# Methods invoked by tests + +sub compile { + my $self = (ref $_[0]? shift : $Last_Self); + my %param = (%{$self}, @_); # Default arguments are from $self + return 1 if $self->errors; + $self->oprint("Compile\n"); + + $self->{sp} = 1 if (join(' ',@{$param{v_flags}},@{$param{v_flags2}}) =~ /-sp\b/); + $self->{trace} = 1 if (join(' ',@{$param{v_flags}},@{$param{v_flags2}}) =~ /-trace\b/); + $self->{coverage} = 1 if (join(' ',@{$param{v_flags}},@{$param{v_flags2}}) =~ /-coverage\b/); + + if ($param{vcs}) { + $self->_make_top(); + $self->_run(logfile=>"obj_dir/".$self->{name}."_vcs_compile.log", + fails=>$param{fails}, + cmd=>["vcs", + @{$param{vcs_flags}}, + @{$param{v_flags}}, + @{$param{v_flags2}}, + $param{top_filename}, + $param{top_shell_filename}, + @{$param{v_other_filenames}}, + ]); + } + if ($param{nc}) { + $self->_make_top(); + $self->_run(logfile=>"obj_dir/".$self->{name}."_nc_compile.log", + fails=>$param{fails}, + cmd=>["ncverilog", + @{$param{nc_flags}}, + @{$param{v_flags}}, + @{$param{v_flags2}}, + $param{top_filename}, + $param{top_shell_filename}, + @{$param{v_other_filenames}}, + ]); + } + if ($param{v3}) { + $opt_gdb="gdbrun" if defined $opt_gdb; + unshift @{$param{verilator_flags}}, "--gdb $opt_gdb" if $opt_gdb; + unshift @{$param{verilator_flags}}, "--debug" if $::Debug; +# unshift @{$param{verilator_flags}}, "--trace"; + if (defined $opt_optimize) { + my $letters = ""; + if ($opt_optimize =~ /[a-zA-Z]/) { + $letters = $opt_optimize; + } else { # Randomly turn on/off different optimizations + foreach my $l ('a'..'z') { + $letters .= ((rand() > 0.5) ? $l : uc $l); + } + unshift @{$param{verilator_flags}}, "--trace" if rand() > 0.5; + unshift @{$param{verilator_flags}}, "--coverage" if rand() > 0.5; + } + unshift @{$param{verilator_flags}}, "--O".$letters; + } + + my @v3args = ("perl","../bin/verilator", + "--prefix ".$self->{VM_PREFIX}, + @{$param{verilator_flags}}, + @{$param{v_flags}}, + @{$param{v_flags2}}, + $param{top_filename}, + @{$param{v_other_filenames}}, + ($param{stdout_filename}?"> ".$param{stdout_filename}:""), + ); + if ($self->sp && !defined $ENV{SYSTEMC}) { + $self->error("Test requires SystemC; ignore error since not installed\n"); + return 1; + } + + $self->_run(logfile=>"obj_dir/".$self->{name}."_simx_compile.log", + fails=>$param{fails}, + expect=>$param{expect}, + cmd=>\@v3args); + return 1 if $self->errors; + + if (!$param{fails} && $param{verilator_make_gcc}) { + if ($param{make_main}) { + $self->_make_main(); + } + if ($self->sp) { + $self->_sp_preproc(%param); + } + $self->oprint("GCC\n"); + $self->_run(logfile=>"obj_dir/".$self->{name}."_simx_gcc.log", + cmd=>["cd obj_dir && ", + "make", "-f".getcwd()."/Makefile_obj", + "VM_PREFIX=$self->{VM_PREFIX}", + ($param{make_main}?"":"MAKE_MAIN=0"), + "$self->{VM_PREFIX}", # not default, as we don't need archive + ]); + } + } + return 1; +} + +sub execute { + my $self = (ref $_[0]? shift : $Last_Self); + return 1 if $self->errors; + my %param = (%{$self}, @_); # Default arguments are from $self + $self->oprint("Run\n"); + if ($param{vcs}) { + #my $fh = IO::File->new("simv.key","w") or die "%Error: $! simv.key,"; + #$fh->print("quit\n"); $fh->close; + $self->_run(logfile=>"obj_dir/".$self->{name}."_simv.log", + cmd=>["./simv",], + %param, + expect=>undef, # vcs expect isn't the same + ); + } + if ($param{v3} + #&& (!$param{needs_v4} || -r "$ENV{VERILATOR_ROOT}/src/V3Gate.cpp") + ) { + $self->_run(logfile=>"obj_dir/".$self->{name}."_simx.log", + cmd=>["obj_dir/$param{VM_PREFIX}", + ], + %param, + ); + } +} + +#---------------------------------------------------------------------- +# Accessors + +sub ok { + my $self = (ref $_[0]? shift : $Last_Self); + $self->{ok} = $_[0] if defined $_[0]; + $self->{ok} = 0 if $self->{errors}; + return $self->{ok}; +} + +sub errors { + my $self = (ref $_[0]? shift : $Last_Self); + return $self->{errors}; +} + +sub top_filename { + my $self = (ref $_[0]? shift : $Last_Self); + $self->{top_filename} = shift if defined $_[0]; + return $self->{top_filename}; +} + +sub sp { + my $self = (ref $_[0]? shift : $Last_Self); + return $self->{sp}; +} + +#---------------------------------------------------------------------- + +sub _run { + my $self = (ref $_[0]? shift : $Last_Self); + my %param = (@_); + my $command = join(' ',@{$param{cmd}}); + print "\t$command\n"; + + if ($param{logfile}) { + open(SAVEOUT, ">&STDOUT") or die "%Error: Can't dup stdout"; + open(SAVEERR, ">&STDERR") or die "%Error: Can't dup stderr"; + if (0) {close(SAVEOUT); close(SAVEERR);} # Prevent unused warning + open(STDOUT, "|tee $param{logfile}") or die "%Error: Can't redirect stdout"; + open(STDERR, ">&STDOUT") or die "%Error: Can't dup stdout"; + autoflush STDOUT 1; + autoflush STDERR 1; + } + + system "$command"; + my $status = $?; + flush STDOUT; + flush STDERR; + + if ($param{logfile}) { + open (STDOUT, ">&SAVEOUT"); + open (STDERR, ">&SAVEERR"); + } + + if (!$param{fails} && $status) { + $self->error("Exec of $param{cmd}[0] failed\n"); + } + if ($param{fails} && $status) { + print "(Exec expected to fail, and did.)\n"; + } + if ($param{fails} && !$status) { + $self->error("Exec of $param{cmd}[0] ok, but expected to fail\n"); + } + return if $self->errors; + + # Read the log file a couple of times to allow for NFS delays + for (my $try=7; $try>=0; $try--) { + sleep 1 if ($try!=7); + my $moretry = $try!=0; + + my $fh = IO::File->new($param{logfile},"r"); + next if !$fh && $moretry; + local $/; undef $/; + my $wholefile = <$fh>; + $fh->close(); + + # Strip debugging comments + $wholefile =~ s/^- [^\n]+\n//mig; + $wholefile =~ s/^- [a-z.0-9]+:\d+:[^\n]+\n//mig; + $wholefile =~ s/^dot [^\n]+\n//mig; + + # Finished? + if ($param{check_finished} && $wholefile !~ /\*\-\* All Finished \*\-\*/) { + next if $moretry; + $self->error("Missing All Finished\n"); + } + if ($param{expect}) { + # Compare + my $quoted = quotemeta ($param{expect}); + my $bad = ($wholefile !~ /$param{expect}/ms + && $wholefile !~ /$quoted/ms); + if ($bad) { + #print "**BAD $self->{name} $param{logfile} MT $moretry $try\n"; + next if $moretry; + $self->error("Mismatch in output from $param{cmd}[0]\n"); + print "GOT:\n"; + print $wholefile; + print "ENDGOT\n"; + print "EXPECT:\n"; + print $param{expect}; + print "ENDEXPECT\n"; + } + } + last; + } +} + +####################################################################### +# Little utilities + +sub _make_main { + my $self = shift; + + $self->_read_inputs(); + + my $filename = "obj_dir/$self->{VM_PREFIX}__main.cpp"; + my $fh = IO::File->new($filename,"w") or die "%Error: $! $filename,"; + + my $VM_PREFIX = $self->{VM_PREFIX}; + print $fh "#include \"$VM_PREFIX.h\"\n"; + + print $fh "// Compile in-place for speed\n"; + print $fh "#include \"verilated.cpp\"\n"; + print $fh "#include \"systemc.h\"\n" if $self->{sc}; + print $fh "#include \"systemperl.h\"\n" if $self->sp; + print $fh "#include \"SpTraceVcdC.cpp\"\n" if $self->{trace}; + print $fh "#include \"SpCoverage.cpp\"\n" if $self->{coverage}; + + print $fh "$VM_PREFIX *top;\n"; + if (!$self->sp) { + print $fh "unsigned int main_time = false;\n"; + print $fh "double sc_time_stamp () {\n"; + print $fh " return main_time;\n"; + print $fh "}\n"; + } + if ($self->sp) { + print $fh "extern int sc_main(int argc, char **argv);\n"; + print $fh "int sc_main(int argc, char **argv) {\n"; + print $fh " sc_signal fastclk;\n" if $self->{inputs}{fastclk}; + print $fh " sc_signal clk;\n" if $self->{inputs}{clk}; + print $fh " sc_time sim_time (1000, SC_NS);\n"; + } else { + print $fh "int main(int argc, char **argv, char **env) {\n"; + print $fh " double sim_time = 1000;\n"; + } + print $fh " Verilated::debug(".($Opt_Verilated_Debug?1:0).");\n"; + print $fh " top = new $VM_PREFIX (\"TOP\");\n"; + my $set; + if ($self->sp) { + print $fh " SP_PIN(top,fastclk,fastclk);\n" if $self->{inputs}{fastclk}; + print $fh " SP_PIN(top,clk,clk);\n" if $self->{inputs}{clk}; + $set = ""; + } else { + print $fh " top->eval();\n"; + $set = "top->"; + } + print $fh " ${set}fastclk = true;\n" if $self->{inputs}{fastclk}; + print $fh " ${set}clk = true;\n" if $self->{inputs}{clk}; + print $fh " while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) {\n"; + for (my $i=0; $i<5; $i++) { + my $action; + if ($self->{inputs}{fastclk}) { + print $fh " ${set}fastclk=!${set}fastclk;\n"; + $action = 1; + } + if ($i==4 && $self->{inputs}{clk}) { + print $fh " ${set}clk=!${set}clk;\n"; + $action = 1; + } + if ($self->sp) { + print $fh " sc_start(1);\n"; + } else { + print $fh " main_time+=1;\n"; + print $fh " ${set}eval();\n" if $action; + } + } + print $fh " }\n"; + print $fh " if (!Verilated::gotFinish()) {\n"; + print $fh ' vl_fatal(__FILE__,__LINE__,"main", "%Error: Timeout; never got a $finish");',"\n"; + print $fh " }\n"; + print $fh " top->final();\n"; + print $fh " SpCoverage::write(\"",$self->{coverage_filename},"\");\n" if $self->{coverage}; + print $fh " exit(0L);\n"; + print $fh "}\n"; + $fh->close(); +} + +####################################################################### + +sub _make_top { + my $self = shift; + + $self->_read_inputs(); + + my $fh = IO::File->new($self->{top_shell_filename},"w") or die "%Error: $! $self->{top_shell_filename},"; + print $fh "module top;\n"; + foreach my $inp (sort (keys %{$self->{inputs}})) { + print $fh " reg ${inp};\n"; + } + # Inst + print $fh " t t (\n"; + my $comma=""; + foreach my $inp (sort (keys %{$self->{inputs}})) { + print $fh "\t${comma}.${inp} (${inp})\n"; + $comma=","; + } + print $fh " );\n"; + + # Test + print $fh " initial begin\n"; + print $fh " fastclk=1;\n" if $self->{inputs}{fastclk}; + print $fh " clk=1;\n" if $self->{inputs}{clk}; + print $fh " while (\$time < 1000) begin\n"; + for (my $i=0; $i<5; $i++) { + print $fh " #1;\n"; + print $fh " fastclk=!fastclk;\n" if $self->{inputs}{fastclk}; + print $fh " clk=!clk;\n" if $i==4 && $self->{inputs}{clk}; + } + print $fh " end\n"; + print $fh " end\n"; + + print $fh "endmodule\n"; + $fh->close(); +} + +####################################################################### + +sub _sp_preproc { + my $self = shift; + my %param = (%{$self}, @_); # Default arguments are from $self + + $self->oprint("Preproc\n"); + + $self->_run(logfile=>"simx.log", + fails=>0, + cmd=>["cd obj_dir ; sp_preproc", + "--preproc", + "$self->{VM_PREFIX}.sp", + ]); +} + +####################################################################### + +sub _read_inputs { + my $self = shift; + my $filename = $self->{top_filename}; + $filename = "t/$filename" if !-r $filename; + my $fh = IO::File->new($filename) or die "%Error: $! $filename,"; + while (defined(my $line = $fh->getline)) { + if ($line =~ /^\s*input\s*(\S+)\s*(\/[^\/]+\/|)\s*;/) { + $self->{inputs}{$1} = $1; + } + if ($line =~ /^\s*(function|task|endmodule)/) { + last; + } + } + $fh->close(); +} + +####################################################################### +# Verilator utilities + +our $_Verilator_Version; +sub verilator_version { + if (!defined $_Verilator_Version) { + my @args = ("perl","../bin/verilator", "--version"); + my $args = join(' ',@args); + $_Verilator_Version = `$args`; + $_Verilator_Version or die "can't fork: $! ".join(' ',@args); + chomp $_Verilator_Version; + } + return $_Verilator_Version if defined $_Verilator_Version; +} + +####################################################################### +# File utilities + +sub files_identical { + my $fn1 = shift; + my $fn2 = shift; + my $f1 = IO::File->new ($fn1) or die "%Error: $! $fn1,"; + my $f2 = IO::File->new ($fn2) or die "%Error: $! $fn2,"; + my @l1 = $f1->getlines(); + my @l2 = $f2->getlines(); + my $nl = $#l1; $nl = $#l2 if ($#l2 > $nl); + for (my $l=0; $l<=$nl; $l++) { + if (($l1[$l]||"") ne ($l2[$l]||"")) { + warn ("%Warning: Line ".($l+1)." mismatches; $fn1 != $fn2\n" + ."F1: ".($l1[$l]||"*EOF*\n") + ."F2: ".($l2[$l]||"*EOF*\n")); + return 0; + } + } + return 1; +} + +sub file_grep_not { + my $self = (ref $_[0]? shift : $Last_Self); + my $filename = shift; + my $regexp = shift; + + my $contents = $self->file_contents($filename); + return if ($contents eq "_Already_Errored_"); + if ($contents =~ /$regexp/) { + $self->error("File_grep_not: $filename: Regexp found: $regexp\n"); + } +} + +sub file_grep { + my $self = (ref $_[0]? shift : $Last_Self); + my $filename = shift; + my $regexp = shift; + + my $contents = $self->file_contents($filename); + return if ($contents eq "_Already_Errored_"); + if ($contents !~ /$regexp/) { + $self->error("File_grep: $filename: Regexp not found: $regexp\n"); + } +} + +my %_File_Contents_Cache; + +sub file_contents { + my $self = (ref $_[0]? shift : $Last_Self); + my $filename = shift; + + if (!$_File_Contents_Cache{$filename}) { + my $fh = IO::File->new($filename,"r"); + if (!$fh) { + $_File_Contents_Cache{$filename} = "_Already_Errored_"; + $self->error("File_grep file not found: ".$filename."\n"); + return $_File_Contents_Cache{$filename}; + } + local $/; undef $/; + my $wholefile = <$fh>; + $fh->close(); + $_File_Contents_Cache{$filename} = $wholefile; + } + + return $_File_Contents_Cache{$filename}; +} + +####################################################################### +####################################################################### +####################################################################### +####################################################################### +# Forker class + +package Forker; +use strict; + +# This is a shell that matches Parallel::Forker. +# If that package is not installed, this runs the tests in *series* + +sub new { + my $class = shift; + my $self = {@_}; + bless $self, $class; + return $self; +} +sub schedule { + my $self = shift; + my %params = (@_); + &{$params{run_on_start}}(); + &{$params{run_on_finish}}(); + return $self; +} +sub max_proc {} +sub sig_child {} +sub kill_tree_all {} +sub wait_all {} +sub ready {} + +####################################################################### +1; +package main; +__END__ + +=pod + +=head1 NAME + +driver.pl - Run regression tests + +=head1 SYNOPSIS + + driver.pl + +=head1 DESCRIPTION + +driver.pl invokes Verilator or another simulator on each little test file. + +=head1 ARGUMENTS + +=over 4 + +=item --gdb + +Run verilator under the debugger. + +=item --help + +Displays this message and program version and exits. + +=item --j # + +Run number of parallel tests. Requires Parallel::Forker project. + +=item --optimize + +Randomly turn on/off different optimizations. With specific flags, +use those optimization settings + +=item --nc + +Run using NC-Verilog. + +=item --stop + +Stop on the first error + +=item --vcs + +Run using VCS. + +=item --v3 + +Run using Verilator. + +=back + +=head1 SEE ALSO + +=head1 AUTHORS + +Wilson Snyder + +=cut + +###################################################################### diff --git a/test_regress/input.vc b/test_regress/input.vc new file mode 100644 index 000000000..7716927ed --- /dev/null +++ b/test_regress/input.vc @@ -0,0 +1,9 @@ + ++librescan +libext+.v + -y t + -y obj_dir/ + +incdir+t + +incdir+../include + +incdir+obj_dir/ + + \ No newline at end of file diff --git a/test_regress/t/t_EXAMPLE.pl b/test_regress/t/t_EXAMPLE.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_EXAMPLE.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_EXAMPLE.v b/test_regress/t/t_EXAMPLE.v new file mode 100644 index 000000000..088a53eec --- /dev/null +++ b/test_regress/t/t_EXAMPLE.v @@ -0,0 +1,63 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// Use this file as a template for submitting bugs, etc. +// This module takes a single clock input, and should either +// $write("*-* All Finished *-*\n"); +// $finish +// on success, or $stop. +// +// **If you do not wish for your code to be released to the public +// please note it here** + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + // Some inputs we'll set to random values + reg [31:0] in_a; + reg [31:0] in_b; + + // Some arbitrary function for testing + // We'll test below that for each random in_a and in_b, we get a good out_a. + wire [31:0] out_x = (in_a ^ in_b); + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + $write("%d %x %x %x\n", cyc, in_a, in_b, out_x); + if (cyc==1) begin + // Assign inputs randomly + in_a <= 32'h89a14fab; + in_b <= 32'h7ab512fa; + end + if (cyc==2) begin + in_a <= 32'hf4c11a42; + in_b <= 32'h359967c6; + // Verify output is correct + if (out_x != 32'hf3145d51) $stop; + end + if (cyc==3) begin + in_a <= 32'h58dca151; + in_b <= 32'hdc687b27; + if (out_x != 32'hc1587d84) $stop; + end + if (cyc==4) begin + in_a <= 32'h09df0bbb; + in_b <= 32'h0d0e7231; + if (out_x != 32'h84b4da76) $stop; + end + if (cyc==5) begin + if (out_x != 32'h04d1798a) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_alw_combdly.pl b/test_regress/t/t_alw_combdly.pl new file mode 100755 index 000000000..eff491d7a --- /dev/null +++ b/test_regress/t/t_alw_combdly.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_alw_combdly.v b/test_regress/t/t_alw_combdly.v new file mode 100644 index 000000000..0a0a30f42 --- /dev/null +++ b/test_regress/t/t_alw_combdly.v @@ -0,0 +1,39 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [31:0] a, b, c; + + always @ (/*AS*/a or b) begin + // verilator lint_off COMBDLY + c <= a | b; + // verilator lint_on COMBDLY + end + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc<=cyc+1; + if (cyc==1) begin + a <= 32'hfeed0000; + b <= 32'h0000face; + end + if (cyc==2) begin + if (c != 32'hfeedface) $stop; + end + if (cyc==7) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_alw_dly.pl b/test_regress/t/t_alw_dly.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_alw_dly.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_alw_dly.v b/test_regress/t/t_alw_dly.v new file mode 100644 index 000000000..145ad51bc --- /dev/null +++ b/test_regress/t/t_alw_dly.v @@ -0,0 +1,68 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg posedge_wr_clocks; + reg prev_wr_clocks; + reg [31:0] m_din; + reg [31:0] m_dout; + + always @(negedge clk) begin + prev_wr_clocks = 0; + end + + reg comb_pos_1; + reg comb_prev_1; + always @ (/*AS*/clk or posedge_wr_clocks or prev_wr_clocks) begin + comb_pos_1 = (clk &~ prev_wr_clocks); + comb_prev_1 = comb_pos_1 | posedge_wr_clocks; + comb_pos_1 = 1'b1; + end + + always @ (posedge clk) begin + posedge_wr_clocks = (clk &~ prev_wr_clocks); //surefire lint_off_line SEQASS + prev_wr_clocks = prev_wr_clocks | posedge_wr_clocks; //surefire lint_off_line SEQASS + if (posedge_wr_clocks) begin + //$write("[%0t] Wrclk\n", $time); + m_dout <= m_din; + end + end + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc<=cyc+1; + if (cyc==1) begin + $write(" %x\n",comb_pos_1); + m_din <= 32'hfeed; + end + if (cyc==2) begin + $write(" %x\n",comb_pos_1); + m_din <= 32'he11e; + end + if (cyc==3) begin + m_din <= 32'he22e; + $write(" %x\n",comb_pos_1); + if (m_dout!=32'hfeed) $stop; + end + if (cyc==4) begin + if (m_dout!=32'he11e) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_alw_split.pl b/test_regress/t/t_alw_split.pl new file mode 100755 index 000000000..b50e64cb2 --- /dev/null +++ b/test_regress/t/t_alw_split.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + v_flags2 => ["--stats"], + ); + +if ($Last_Self->{v3}) { + file_grep ($Last_Self->{stats}, qr/Optimizations, Split always\s+6/i); +} + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_alw_split.v b/test_regress/t/t_alw_split.v new file mode 100644 index 000000000..3a588caa1 --- /dev/null +++ b/test_regress/t/t_alw_split.v @@ -0,0 +1,136 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [15:0] m_din; + + // OK + reg [15:0] a_split_1, a_split_2; + always @ (/*AS*/m_din) begin + a_split_1 = m_din; + a_split_2 = m_din; + end + + // OK + reg [15:0] b_split_1, b_split_2; + always @ (/*AS*/m_din) begin + b_split_1 = m_din; + b_split_2 = b_split_1; + end + + // Not OK + reg [15:0] c_split_1, c_split_2; + always @ (/*AS*/m_din) begin + c_split_1 = m_din; + c_split_2 = c_split_1; + c_split_1 = ~m_din; + end + + // OK + reg [15:0] d_split_1, d_split_2; + always @ (posedge clk) begin + d_split_1 <= m_din; + d_split_2 <= d_split_1; + d_split_1 <= ~m_din; + end + + // Not OK + always @ (posedge clk) begin + $write(" foo %x", m_din); + $write(" bar %x\n", m_din); + end + + // Not OK + reg [15:0] e_split_1, e_split_2; + always @ (posedge clk) begin + e_split_1 = m_din; + e_split_2 = e_split_1; + end + + // Not OK + reg [15:0] f_split_1, f_split_2; + always @ (posedge clk) begin + f_split_2 = f_split_1; + f_split_1 = m_din; + end + + // Not Ok + reg [15:0] l_split_1, l_split_2; + always @ (posedge clk) begin + l_split_2 <= l_split_1; + l_split_1 <= l_split_2 | m_din; + end + + // OK + reg [15:0] z_split_1, z_split_2; + always @ (posedge clk) begin + z_split_1 <= 0; + z_split_1 <= ~m_din; + end + always @ (posedge clk) begin + z_split_2 <= 0; + z_split_2 <= z_split_1; + end + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc<=cyc+1; + if (cyc==1) begin + m_din <= 16'hfeed; + end + if (cyc==3) begin + end + if (cyc==4) begin + m_din <= 16'he11e; + //$write(" A %x %x\n", a_split_1, a_split_2); + if (!(a_split_1==16'hfeed && a_split_2==16'hfeed)) $stop; + if (!(b_split_1==16'hfeed && b_split_2==16'hfeed)) $stop; + if (!(c_split_1==16'h0112 && c_split_2==16'hfeed)) $stop; + if (!(d_split_1==16'h0112 && d_split_2==16'h0112)) $stop; + if (!(e_split_1==16'hfeed && e_split_2==16'hfeed)) $stop; + if (!(f_split_1==16'hfeed && f_split_2==16'hfeed)) $stop; + if (!(z_split_1==16'h0112 && z_split_2==16'h0112)) $stop; + end + if (cyc==5) begin + m_din <= 16'he22e; + if (!(a_split_1==16'he11e && a_split_2==16'he11e)) $stop; + if (!(b_split_1==16'he11e && b_split_2==16'he11e)) $stop; + if (!(c_split_1==16'h1ee1 && c_split_2==16'he11e)) $stop; + if (!(d_split_1==16'h0112 && d_split_2==16'h0112)) $stop; + if (!(z_split_1==16'h0112 && z_split_2==16'h0112)) $stop; + // Two valid orderings, as we don't know which posedge clk gets evaled first + if (!(e_split_1==16'hfeed && e_split_2==16'hfeed) && !(e_split_1==16'he11e && e_split_2==16'he11e)) $stop; + if (!(f_split_1==16'hfeed && f_split_2==16'hfeed) && !(f_split_1==16'he11e && f_split_2==16'hfeed)) $stop; + end + if (cyc==6) begin + m_din <= 16'he33e; + if (!(a_split_1==16'he22e && a_split_2==16'he22e)) $stop; + if (!(b_split_1==16'he22e && b_split_2==16'he22e)) $stop; + if (!(c_split_1==16'h1dd1 && c_split_2==16'he22e)) $stop; + if (!(d_split_1==16'h1ee1 && d_split_2==16'h0112)) $stop; + if (!(z_split_1==16'h1ee1 && d_split_2==16'h0112)) $stop; + // Two valid orderings, as we don't know which posedge clk gets evaled first + if (!(e_split_1==16'he11e && e_split_2==16'he11e) && !(e_split_1==16'he22e && e_split_2==16'he22e)) $stop; + if (!(f_split_1==16'he11e && f_split_2==16'hfeed) && !(f_split_1==16'he22e && f_split_2==16'he11e)) $stop; + end + if (cyc==7) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_alw_splitord.pl b/test_regress/t/t_alw_splitord.pl new file mode 100755 index 000000000..981bdd011 --- /dev/null +++ b/test_regress/t/t_alw_splitord.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + v_flags2 => ["--stats"], + ); + +if ($Last_Self->{v3}) { + file_grep ($Last_Self->{stats}, qr/Optimizations, Split always\s+0/i); +} + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_alw_splitord.v b/test_regress/t/t_alw_splitord.v new file mode 100644 index 000000000..3f0cbec20 --- /dev/null +++ b/test_regress/t/t_alw_splitord.v @@ -0,0 +1,156 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003-2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [15:0] m_din; + + // OK + reg [15:0] c_split_1, c_split_2, c_split_3, c_split_4, c_split_5; + always @ (posedge clk) begin + if (cyc==0) begin + /*AUTORESET*/ + // Beginning of autoreset for uninitialized flops + c_split_1 <= 16'h0; + c_split_2 <= 16'h0; + c_split_3 <= 16'h0; + c_split_4 <= 0; + c_split_5 <= 0; + // End of automatics + end + else begin + c_split_1 <= m_din; + c_split_2 <= c_split_1; + c_split_3 <= c_split_2 & {16{(cyc!=0)}}; + if (cyc==1) begin + c_split_4 <= 16'h4; + c_split_5 <= 16'h5; + end + else begin + c_split_4 <= c_split_3; + c_split_5 <= c_split_4; + end + end + end + + // OK + reg [15:0] d_split_1, d_split_2; + always @ (posedge clk) begin + if (cyc==0) begin + /*AUTORESET*/ + // Beginning of autoreset for uninitialized flops + d_split_1 <= 16'h0; + d_split_2 <= 16'h0; + // End of automatics + end + else begin + d_split_1 <= m_din; + d_split_2 <= d_split_1; + d_split_1 <= ~m_din; + end + end + + // Not OK + always @ (posedge clk) begin + if (cyc==0) begin + /*AUTORESET*/ + // Beginning of autoreset for uninitialized flops + // End of automatics + end + else begin + $write(" foo %x", m_din); + $write(" bar %x\n", m_din); + end + end + + // Not OK + reg [15:0] e_split_1, e_split_2; + always @ (posedge clk) begin + if (cyc==0) begin + /*AUTORESET*/ + // Beginning of autoreset for uninitialized flops + e_split_1 = 16'h0; + e_split_2 = 16'h0; + // End of automatics + end + else begin + e_split_1 = m_din; + e_split_2 = e_split_1; + end + end + + // Not OK + reg [15:0] f_split_1, f_split_2; + always @ (posedge clk) begin + if (cyc==0) begin + /*AUTORESET*/ + // Beginning of autoreset for uninitialized flops + f_split_1 = 16'h0; + f_split_2 = 16'h0; + // End of automatics + end + else begin + f_split_2 = f_split_1; + f_split_1 = m_din; + end + end + + always @ (posedge clk) begin + if (cyc!=0) begin + //$write(" C %d %x %x\n", cyc, c_split_1, c_split_2); + cyc<=cyc+1; + if (cyc==1) begin + m_din <= 16'hfeed; + end + if (cyc==3) begin + end + if (cyc==4) begin + m_din <= 16'he11e; + if (!(d_split_1==16'h0112 && d_split_2==16'h0112)) $stop; + if (!(e_split_1==16'hfeed && e_split_2==16'hfeed)) $stop; + if (!(f_split_1==16'hfeed && f_split_2==16'hfeed)) $stop; + end + if (cyc==5) begin + m_din <= 16'he22e; + if (!(d_split_1==16'h0112 && d_split_2==16'h0112)) $stop; + // Two valid orderings, as we don't know which posedge clk gets evaled first + if (!(e_split_1==16'hfeed && e_split_2==16'hfeed) && !(e_split_1==16'he11e && e_split_2==16'he11e)) $stop; + if (!(f_split_1==16'hfeed && f_split_2==16'hfeed) && !(f_split_1==16'he11e && f_split_2==16'hfeed)) $stop; + end + if (cyc==6) begin + m_din <= 16'he33e; + if (!(c_split_1==16'he11e && c_split_2==16'hfeed && c_split_3==16'hfeed)) $stop; + if (!(d_split_1==16'h1ee1 && d_split_2==16'h0112)) $stop; + // Two valid orderings, as we don't know which posedge clk gets evaled first + if (!(e_split_1==16'he11e && e_split_2==16'he11e) && !(e_split_1==16'he22e && e_split_2==16'he22e)) $stop; + if (!(f_split_1==16'he11e && f_split_2==16'hfeed) && !(f_split_1==16'he22e && f_split_2==16'he11e)) $stop; + end + if (cyc==7) begin + m_din <= 16'he44e; + if (!(c_split_1==16'he22e && c_split_2==16'he11e && c_split_3==16'hfeed)) $stop; + end + if (cyc==8) begin + m_din <= 16'he55e; + if (!(c_split_1==16'he33e && c_split_2==16'he22e && c_split_3==16'he11e + && c_split_4==16'hfeed && c_split_5==16'hfeed)) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_assert_synth.pl b/test_regress/t/t_assert_synth.pl new file mode 100755 index 000000000..ed1003927 --- /dev/null +++ b/test_regress/t/t_assert_synth.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +compile ( + v_flags2 => [$Last_Self->{v3}?'--assert':($Last_Self->{nc}?'+assert':'')], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_assert_synth.v b/test_regress/t/t_assert_synth.v new file mode 100644 index 000000000..e1bea1920 --- /dev/null +++ b/test_regress/t/t_assert_synth.v @@ -0,0 +1,96 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg a; initial a = 1'b1; + reg b_fc; initial b_fc = 1'b0; + reg b_pc; initial b_pc = 1'b0; + reg b_oh; initial b_oh = 1'b0; + reg b_oc; initial b_oc = 1'b0; + wire a_l = ~a; + wire b_oc_l = ~b_oc; + + // Note we must insure that full, parallel, etc, only fire during + // edges (not mid-cycle), and must provide a way to turn them off. + // SystemVerilog provides: $asserton and $assertoff. + + // verilator lint_off CASEINCOMPLETE + + always @* begin + // Note not all tools support directives on casez's + case ({a,b_fc}) // synopsys full_case + 2'b0_0: ; + 2'b0_1: ; + 2'b1_0: ; + // Note no default + endcase + end + + always @* begin + case (1'b1) // synopsys full_case parallel_case + a: ; + b_pc: ; + endcase + end + +`ifdef NOT_YET_verilator + // ambit synthesis one_hot "a, b_oh" + // cadence one_cold "a_l, b_oc_l" +`endif + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + a <= 1'b1; + b_fc <= 1'b0; + b_pc <= 1'b0; + b_oh <= 1'b0; + b_oc <= 1'b0; + end + if (cyc==2) begin + a <= 1'b0; + b_fc <= 1'b1; + b_pc <= 1'b1; + b_oh <= 1'b1; + b_oc <= 1'b1; + end + if (cyc==3) begin + a <= 1'b1; + b_fc <= 1'b0; + b_pc <= 1'b0; + b_oh <= 1'b0; + b_oc <= 1'b0; + end + if (cyc==4) begin +`ifdef FAILING_FULL + b_fc <= 1'b1; +`endif +`ifdef FAILING_PARALLEL + b_pc <= 1'b1; +`endif +`ifdef FAILING_OH + b_oh <= 1'b1; +`endif +`ifdef FAILING_OC + b_oc <= 1'b1; +`endif + end + if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_assert_synth_full.pl b/test_regress/t/t_assert_synth_full.pl new file mode 100755 index 000000000..c6d7afdaa --- /dev/null +++ b/test_regress/t/t_assert_synth_full.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +top_filename("t/t_assert_synth.v"); + +compile ( + v_flags2 => [$Last_Self->{v3}?'--assert':($Last_Self->{nc}?'+assert':''), + '+define+FAILING_FULL',], + ); + +execute ( + check_finished=>0, + fails=>1, + expect=> +'%Error: t_assert_synth.v:\d+: Assertion failed in TOP.v: synthesis full_case' + ); + +ok(1); +1; diff --git a/test_regress/t/t_assert_synth_off.pl b/test_regress/t/t_assert_synth_off.pl new file mode 100755 index 000000000..415226422 --- /dev/null +++ b/test_regress/t/t_assert_synth_off.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +top_filename("t/t_assert_synth.v"); + +compile ( + v_flags2 => ['+define+FAILING_FULL', + '+define+FAILING_PARALLEL', + '+define+FAILING_OH', + '+define+FAILING_OC', + ], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_assert_synth_parallel.pl b/test_regress/t/t_assert_synth_parallel.pl new file mode 100755 index 000000000..7e03ee2f8 --- /dev/null +++ b/test_regress/t/t_assert_synth_parallel.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +top_filename("t/t_assert_synth.v"); + +compile ( + v_flags2 => [$Last_Self->{v3}?'--assert':($Last_Self->{nc}?'+assert':''), + '+define+FAILING_PARALLEL',], + ); + +execute ( + check_finished=>0, + fails=>1, + expect=> +'%Error: t_assert_synth.v:\d+: Assertion failed in TOP.v: synthesis parallel_case' + ); + +ok(1); +1; diff --git a/test_regress/t/t_bitsel_loop.pl b/test_regress/t/t_bitsel_loop.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_bitsel_loop.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_bitsel_loop.v b/test_regress/t/t_bitsel_loop.v new file mode 100644 index 000000000..07e0ceef4 --- /dev/null +++ b/test_regress/t/t_bitsel_loop.v @@ -0,0 +1,53 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [255:0] a; + reg [255:0] q; + reg [63:0] qq; + + integer i; + always @* begin + for (i=0; i<256; i=i+1) begin + q[255-i] = a[i]; + end + q[27:16] = 12'hfed; + for (i=0; i<64; i=i+1) begin + qq[63-i] = a[i]; + end + qq[27:16] = 12'hfed; + end + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + $write("%x/%x %x\n", q, qq, a); + if (cyc==1) begin + a = 256'hed388e646c843d35de489bab2413d77045e0eb7642b148537491f3da147e7f26; + end + if (cyc==2) begin + a = 256'h0e17c88f3d5fe51a982646c8e2bd68c3e236ddfddddbdad20a48e039c9f395b8; + if (q != 256'h64fe7e285bcf892eca128d426ed707a20eebc824d5d9127bacbc21362fed1cb7) $stop; + if (qq != 64'h64fe7e285fed892e) $stop; + end + if (cyc==3) begin + if (q != 256'h1da9cf939c0712504b5bdbbbbfbb6c47c316bd471362641958a7fabcffede870) $stop; + if (qq != 64'h1da9cf939fed1250) $stop; + end + if (cyc==4) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_case_66bits.pl b/test_regress/t/t_case_66bits.pl new file mode 100755 index 000000000..6efe76c31 --- /dev/null +++ b/test_regress/t/t_case_66bits.pl @@ -0,0 +1,12 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_case_66bits.v b/test_regress/t/t_case_66bits.v new file mode 100644 index 000000000..a27eb813f --- /dev/null +++ b/test_regress/t/t_case_66bits.v @@ -0,0 +1,27 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [65:0] idx /*verilator public*/; initial idx = 1; + + always @(posedge clk) begin + case(idx) + 1: idx = 100; + 100: begin + $write("*-* All Finished *-*\n"); + $finish; + end + default: $stop; + endcase + end + +endmodule diff --git a/test_regress/t/t_case_auto1.pl b/test_regress/t/t_case_auto1.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_case_auto1.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_case_auto1.v b/test_regress/t/t_case_auto1.v new file mode 100644 index 000000000..218982264 --- /dev/null +++ b/test_regress/t/t_case_auto1.v @@ -0,0 +1,76 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + localparam // synopsys enum En_State + EP_State_IDLE = {3'b000,5'd00}, + EP_State_CMDSHIFT0 = {3'b001,5'd00}, + EP_State_CMDSHIFT13 = {3'b001,5'd13}, + EP_State_CMDSHIFT14 = {3'b001,5'd14}, + EP_State_CMDSHIFT15 = {3'b001,5'd15}, + EP_State_CMDSHIFT16 = {3'b001,5'd16}, + EP_State_DWAIT = {3'b010,5'd00}, + EP_State_DSHIFT0 = {3'b100,5'd00}, + EP_State_DSHIFT1 = {3'b100,5'd01}, + EP_State_DSHIFT15 = {3'b100,5'd15}; + + reg [7:0] /* synopsys enum En_State */ + m_state_xr; // Last command, for debugging + /*AUTOASCIIENUM("m_state_xr", "m_stateAscii_xr", "EP_State_")*/ + // Beginning of automatic ASCII enum decoding + reg [79:0] m_stateAscii_xr; // Decode of m_state_xr + always @(m_state_xr) begin + case ({m_state_xr}) + EP_State_IDLE: m_stateAscii_xr = "idle "; + EP_State_CMDSHIFT0: m_stateAscii_xr = "cmdshift0 "; + EP_State_CMDSHIFT13: m_stateAscii_xr = "cmdshift13"; + EP_State_CMDSHIFT14: m_stateAscii_xr = "cmdshift14"; + EP_State_CMDSHIFT15: m_stateAscii_xr = "cmdshift15"; + EP_State_CMDSHIFT16: m_stateAscii_xr = "cmdshift16"; + EP_State_DWAIT: m_stateAscii_xr = "dwait "; + EP_State_DSHIFT0: m_stateAscii_xr = "dshift0 "; + EP_State_DSHIFT1: m_stateAscii_xr = "dshift1 "; + EP_State_DSHIFT15: m_stateAscii_xr = "dshift15 "; + default: m_stateAscii_xr = "%Error "; + endcase + end + // End of automatics + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + //$write("%d %x %x %x\n", cyc, data, wrapcheck_a, wrapcheck_b); + if (cyc==1) begin + m_state_xr <= EP_State_IDLE; + end + if (cyc==2) begin + if (m_stateAscii_xr != "idle ") $stop; + m_state_xr <= EP_State_CMDSHIFT13; + end + if (cyc==3) begin + if (m_stateAscii_xr != "cmdshift13") $stop; + m_state_xr <= EP_State_CMDSHIFT16; + end + if (cyc==4) begin + if (m_stateAscii_xr != "cmdshift16") $stop; + m_state_xr <= EP_State_DWAIT; + end + if (cyc==9) begin + if (m_stateAscii_xr != "dwait ") $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_case_default_bad.pl b/test_regress/t/t_case_default_bad.pl new file mode 100755 index 000000000..f13c48bae --- /dev/null +++ b/test_regress/t/t_case_default_bad.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>1, + expect=> +'%Error: t/t_case_default_bad.v:\d+: Multiple default statements in case statement. +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_case_default_bad.v b/test_regress/t/t_case_default_bad.v new file mode 100644 index 000000000..d732cfa9f --- /dev/null +++ b/test_regress/t/t_case_default_bad.v @@ -0,0 +1,19 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + value + ); + input [3:0] value; + always @ (/*AS*/value) begin + case (value) + default: $stop; + 4'd0000: $stop; + default: $stop; + endcase + end +endmodule diff --git a/test_regress/t/t_case_genx_bad.pl b/test_regress/t/t_case_genx_bad.pl new file mode 100755 index 000000000..4cb5dde90 --- /dev/null +++ b/test_regress/t/t_case_genx_bad.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +compile ( + fails=>1, + expect=> +'%Error: t/t_case_genx_bad.v:\d+: Use of x/\? constant in generate case statement, \(no such thing as \'generate casez\'\) +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_case_genx_bad.v b/test_regress/t/t_case_genx_bad.v new file mode 100644 index 000000000..6567a1e71 --- /dev/null +++ b/test_regress/t/t_case_genx_bad.v @@ -0,0 +1,19 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005-2006 by Wilson Snyder. + +module t (/*AUTOARG*/); + + parameter P = 32'b1000; + + generate + case (P) + 32'b0: initial begin end + 32'b1xxx: initial begin end + default: initial begin end + endcase + endgenerate + +endmodule diff --git a/test_regress/t/t_case_huge.pl b/test_regress/t/t_case_huge.pl new file mode 100755 index 000000000..27a32c742 --- /dev/null +++ b/test_regress/t/t_case_huge.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + v_flags2 => ["--stats --profile-cfuncs"], + ); + +if ($Last_Self->{v3}) { + file_grep ($Last_Self->{stats}, qr/Optimizations, Tables created\s+10/i); + file_grep ($Last_Self->{stats}, qr/Optimizations, Combined CFuncs\s+10/i); +} + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_case_huge.v b/test_regress/t/t_case_huge.v new file mode 100644 index 000000000..3e305b3f4 --- /dev/null +++ b/test_regress/t/t_case_huge.v @@ -0,0 +1,212 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [9:0] index; + wire [7:0] index0 = index[7:0] + 8'h0; + wire [7:0] index1 = index[7:0] + 8'h1; + wire [7:0] index2 = index[7:0] + 8'h2; + wire [7:0] index3 = index[7:0] + 8'h3; + wire [7:0] index4 = index[7:0] + 8'h4; + wire [7:0] index5 = index[7:0] + 8'h5; + wire [7:0] index6 = index[7:0] + 8'h6; + wire [7:0] index7 = index[7:0] + 8'h7; + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [9:0] outa0; // From s0 of t_case_huge_sub.v + wire [9:0] outa1; // From s1 of t_case_huge_sub.v + wire [9:0] outa2; // From s2 of t_case_huge_sub.v + wire [9:0] outa3; // From s3 of t_case_huge_sub.v + wire [9:0] outa4; // From s4 of t_case_huge_sub.v + wire [9:0] outa5; // From s5 of t_case_huge_sub.v + wire [9:0] outa6; // From s6 of t_case_huge_sub.v + wire [9:0] outa7; // From s7 of t_case_huge_sub.v + wire [1:0] outb0; // From s0 of t_case_huge_sub.v + wire [1:0] outb1; // From s1 of t_case_huge_sub.v + wire [1:0] outb2; // From s2 of t_case_huge_sub.v + wire [1:0] outb3; // From s3 of t_case_huge_sub.v + wire [1:0] outb4; // From s4 of t_case_huge_sub.v + wire [1:0] outb5; // From s5 of t_case_huge_sub.v + wire [1:0] outb6; // From s6 of t_case_huge_sub.v + wire [1:0] outb7; // From s7 of t_case_huge_sub.v + wire outc0; // From s0 of t_case_huge_sub.v + wire outc1; // From s1 of t_case_huge_sub.v + wire outc2; // From s2 of t_case_huge_sub.v + wire outc3; // From s3 of t_case_huge_sub.v + wire outc4; // From s4 of t_case_huge_sub.v + wire outc5; // From s5 of t_case_huge_sub.v + wire outc6; // From s6 of t_case_huge_sub.v + wire outc7; // From s7 of t_case_huge_sub.v + wire [9:0] outq; // From q of t_case_huge_sub4.v + wire [3:0] outr; // From sub3 of t_case_huge_sub3.v + wire [9:0] outsmall; // From sub2 of t_case_huge_sub2.v + // End of automatics + + t_case_huge_sub2 sub2 ( + // Outputs + .outa (outsmall[9:0]), + /*AUTOINST*/ + // Inputs + .index (index[9:0])); + + t_case_huge_sub3 sub3 (/*AUTOINST*/ + // Outputs + .outr (outr[3:0]), + // Inputs + .clk (clk), + .index (index[9:0])); + + /* t_case_huge_sub AUTO_TEMPLATE ( + .outa (outa@[]), + .outb (outb@[]), + .outc (outc@[]), + .index (index@[])); + */ + + t_case_huge_sub s0 (/*AUTOINST*/ + // Outputs + .outa (outa0[9:0]), // Templated + .outb (outb0[1:0]), // Templated + .outc (outc0), // Templated + // Inputs + .index (index0[7:0])); // Templated + t_case_huge_sub s1 (/*AUTOINST*/ + // Outputs + .outa (outa1[9:0]), // Templated + .outb (outb1[1:0]), // Templated + .outc (outc1), // Templated + // Inputs + .index (index1[7:0])); // Templated + t_case_huge_sub s2 (/*AUTOINST*/ + // Outputs + .outa (outa2[9:0]), // Templated + .outb (outb2[1:0]), // Templated + .outc (outc2), // Templated + // Inputs + .index (index2[7:0])); // Templated + t_case_huge_sub s3 (/*AUTOINST*/ + // Outputs + .outa (outa3[9:0]), // Templated + .outb (outb3[1:0]), // Templated + .outc (outc3), // Templated + // Inputs + .index (index3[7:0])); // Templated + t_case_huge_sub s4 (/*AUTOINST*/ + // Outputs + .outa (outa4[9:0]), // Templated + .outb (outb4[1:0]), // Templated + .outc (outc4), // Templated + // Inputs + .index (index4[7:0])); // Templated + t_case_huge_sub s5 (/*AUTOINST*/ + // Outputs + .outa (outa5[9:0]), // Templated + .outb (outb5[1:0]), // Templated + .outc (outc5), // Templated + // Inputs + .index (index5[7:0])); // Templated + t_case_huge_sub s6 (/*AUTOINST*/ + // Outputs + .outa (outa6[9:0]), // Templated + .outb (outb6[1:0]), // Templated + .outc (outc6), // Templated + // Inputs + .index (index6[7:0])); // Templated + t_case_huge_sub s7 (/*AUTOINST*/ + // Outputs + .outa (outa7[9:0]), // Templated + .outb (outb7[1:0]), // Templated + .outc (outc7), // Templated + // Inputs + .index (index7[7:0])); // Templated + + t_case_huge_sub4 q (/*AUTOINST*/ + // Outputs + .outq (outq[9:0]), + // Inputs + .index (index[7:0])); + + + integer cyc; initial cyc=1; + initial index = 10'h0; + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + //$write("%x: %x\n",cyc,outr); + //$write("%x: %x %x %x %x\n", cyc, outa1,outb1,outc1,index1); + if (cyc==1) begin + index <= 10'h236; + end + if (cyc==2) begin + index <= 10'h022; + if (outsmall != 10'h282) $stop; + if (outr != 4'b0) $stop; + if ({outa0,outb0,outc0}!={10'h282,2'd3,1'b0}) $stop; + if ({outa1,outb1,outc1}!={10'h21c,2'd3,1'b1}) $stop; + if ({outa2,outb2,outc2}!={10'h148,2'd0,1'b1}) $stop; + if ({outa3,outb3,outc3}!={10'h3c0,2'd2,1'b0}) $stop; + if ({outa4,outb4,outc4}!={10'h176,2'd1,1'b1}) $stop; + if ({outa5,outb5,outc5}!={10'h3fc,2'd2,1'b1}) $stop; + if ({outa6,outb6,outc6}!={10'h295,2'd3,1'b1}) $stop; + if ({outa7,outb7,outc7}!={10'h113,2'd2,1'b1}) $stop; + if (outq != 10'h001) $stop; + end + if (cyc==3) begin + index <= 10'h165; + if (outsmall != 10'h191) $stop; + if (outr != 4'h5) $stop; + if ({outa1,outb1,outc1}!={10'h379,2'd1,1'b0}) $stop; + if ({outa2,outb2,outc2}!={10'h073,2'd0,1'b0}) $stop; + if ({outa3,outb3,outc3}!={10'h2fd,2'd3,1'b1}) $stop; + if ({outa4,outb4,outc4}!={10'h2e0,2'd3,1'b1}) $stop; + if ({outa5,outb5,outc5}!={10'h337,2'd1,1'b1}) $stop; + if ({outa6,outb6,outc6}!={10'h2c7,2'd3,1'b1}) $stop; + if ({outa7,outb7,outc7}!={10'h19e,2'd3,1'b0}) $stop; + if (outq != 10'h001) $stop; + end + if (cyc==4) begin + index <= 10'h201; + if (outsmall != 10'h268) $stop; + if (outr != 4'h2) $stop; + if ({outa1,outb1,outc1}!={10'h111,2'd1,1'b0}) $stop; + if ({outa2,outb2,outc2}!={10'h1f9,2'd0,1'b0}) $stop; + if ({outa3,outb3,outc3}!={10'h232,2'd0,1'b1}) $stop; + if ({outa4,outb4,outc4}!={10'h255,2'd3,1'b0}) $stop; + if ({outa5,outb5,outc5}!={10'h34c,2'd1,1'b1}) $stop; + if ({outa6,outb6,outc6}!={10'h049,2'd1,1'b1}) $stop; + if ({outa7,outb7,outc7}!={10'h197,2'd3,1'b0}) $stop; + if (outq != 10'h001) $stop; + end + if (cyc==5) begin + index <= 10'h3ff; + if (outr != 4'hd) $stop; + if (outq != 10'h001) $stop; + end + if (cyc==6) begin + index <= 10'h0; + if (outr != 4'hd) $stop; + if (outq != 10'h114) $stop; + end + if (cyc==7) begin + if (outr != 4'h4) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule + + diff --git a/test_regress/t/t_case_huge_sub.v b/test_regress/t/t_case_huge_sub.v new file mode 100644 index 000000000..819da03fc --- /dev/null +++ b/test_regress/t/t_case_huge_sub.v @@ -0,0 +1,292 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t_case_huge_sub (/*AUTOARG*/ + // Outputs + outa, outb, outc, + // Inputs + index + ); + + input [7:0] index; + output [9:0] outa; + output [1:0] outb; + output outc; + + // ============================= + /*AUTOREG*/ + // Beginning of automatic regs (for this module's undeclared outputs) + reg [9:0] outa; + reg [1:0] outb; + reg outc; + // End of automatics + + // ============================= + // Created from perl + // for $i (0..1023) { printf "\t10'h%03x: begin outa = 10'h%03x; outb = 2'b%02b; outc = 1'b%d; end\n", $i, rand(1024),rand(4),rand(2); }; + + always @(/*AS*/index) begin + case (index) + 8'h00: begin outa = 10'h152; outb = 2'b00; outc = 1'b1; end + 8'h01: begin outa = 10'h318; outb = 2'b11; outc = 1'b1; end + 8'h02: begin outa = 10'h29f; outb = 2'b11; outc = 1'b0; end + 8'h03: begin outa = 10'h392; outb = 2'b01; outc = 1'b1; end + 8'h04: begin outa = 10'h1ef; outb = 2'b00; outc = 1'b0; end + 8'h05: begin outa = 10'h06c; outb = 2'b10; outc = 1'b1; end + 8'h06: begin outa = 10'h29f; outb = 2'b11; outc = 1'b0; end + 8'h07: begin outa = 10'h29a; outb = 2'b10; outc = 1'b0; end + 8'h08: begin outa = 10'h3ce; outb = 2'b01; outc = 1'b0; end + 8'h09: begin outa = 10'h37c; outb = 2'b01; outc = 1'b0; end + 8'h0a: begin outa = 10'h058; outb = 2'b10; outc = 1'b0; end + 8'h0b: begin outa = 10'h3b2; outb = 2'b01; outc = 1'b1; end + 8'h0c: begin outa = 10'h36f; outb = 2'b11; outc = 1'b0; end + 8'h0d: begin outa = 10'h2c5; outb = 2'b11; outc = 1'b0; end + 8'h0e: begin outa = 10'h23a; outb = 2'b00; outc = 1'b0; end + 8'h0f: begin outa = 10'h222; outb = 2'b01; outc = 1'b1; end + 8'h10: begin outa = 10'h328; outb = 2'b00; outc = 1'b1; end + 8'h11: begin outa = 10'h3c3; outb = 2'b00; outc = 1'b1; end + 8'h12: begin outa = 10'h12c; outb = 2'b01; outc = 1'b0; end + 8'h13: begin outa = 10'h1d0; outb = 2'b00; outc = 1'b1; end + 8'h14: begin outa = 10'h3ff; outb = 2'b01; outc = 1'b1; end + 8'h15: begin outa = 10'h115; outb = 2'b11; outc = 1'b1; end + 8'h16: begin outa = 10'h3ba; outb = 2'b10; outc = 1'b0; end + 8'h17: begin outa = 10'h3ba; outb = 2'b00; outc = 1'b0; end + 8'h18: begin outa = 10'h10d; outb = 2'b00; outc = 1'b1; end + 8'h19: begin outa = 10'h13b; outb = 2'b01; outc = 1'b1; end + 8'h1a: begin outa = 10'h0a0; outb = 2'b10; outc = 1'b1; end + 8'h1b: begin outa = 10'h264; outb = 2'b11; outc = 1'b0; end + 8'h1c: begin outa = 10'h3a2; outb = 2'b10; outc = 1'b0; end + 8'h1d: begin outa = 10'h07c; outb = 2'b00; outc = 1'b1; end + 8'h1e: begin outa = 10'h291; outb = 2'b00; outc = 1'b0; end + 8'h1f: begin outa = 10'h1d1; outb = 2'b10; outc = 1'b0; end + 8'h20: begin outa = 10'h354; outb = 2'b11; outc = 1'b1; end + 8'h21: begin outa = 10'h0c0; outb = 2'b00; outc = 1'b1; end + 8'h22: begin outa = 10'h191; outb = 2'b00; outc = 1'b0; end + 8'h23: begin outa = 10'h379; outb = 2'b01; outc = 1'b0; end + 8'h24: begin outa = 10'h073; outb = 2'b00; outc = 1'b0; end + 8'h25: begin outa = 10'h2fd; outb = 2'b11; outc = 1'b1; end + 8'h26: begin outa = 10'h2e0; outb = 2'b11; outc = 1'b1; end + 8'h27: begin outa = 10'h337; outb = 2'b01; outc = 1'b1; end + 8'h28: begin outa = 10'h2c7; outb = 2'b11; outc = 1'b1; end + 8'h29: begin outa = 10'h19e; outb = 2'b11; outc = 1'b0; end + 8'h2a: begin outa = 10'h107; outb = 2'b10; outc = 1'b0; end + 8'h2b: begin outa = 10'h06a; outb = 2'b01; outc = 1'b1; end + 8'h2c: begin outa = 10'h1c7; outb = 2'b01; outc = 1'b1; end + 8'h2d: begin outa = 10'h107; outb = 2'b10; outc = 1'b0; end + 8'h2e: begin outa = 10'h0cf; outb = 2'b01; outc = 1'b1; end + 8'h2f: begin outa = 10'h009; outb = 2'b11; outc = 1'b1; end + 8'h30: begin outa = 10'h09d; outb = 2'b00; outc = 1'b1; end + 8'h31: begin outa = 10'h28e; outb = 2'b00; outc = 1'b0; end + 8'h32: begin outa = 10'h010; outb = 2'b01; outc = 1'b0; end + 8'h33: begin outa = 10'h1e0; outb = 2'b10; outc = 1'b0; end + 8'h34: begin outa = 10'h079; outb = 2'b01; outc = 1'b1; end + 8'h35: begin outa = 10'h13e; outb = 2'b10; outc = 1'b1; end + 8'h36: begin outa = 10'h282; outb = 2'b11; outc = 1'b0; end + 8'h37: begin outa = 10'h21c; outb = 2'b11; outc = 1'b1; end + 8'h38: begin outa = 10'h148; outb = 2'b00; outc = 1'b1; end + 8'h39: begin outa = 10'h3c0; outb = 2'b10; outc = 1'b0; end + 8'h3a: begin outa = 10'h176; outb = 2'b01; outc = 1'b1; end + 8'h3b: begin outa = 10'h3fc; outb = 2'b10; outc = 1'b1; end + 8'h3c: begin outa = 10'h295; outb = 2'b11; outc = 1'b1; end + 8'h3d: begin outa = 10'h113; outb = 2'b10; outc = 1'b1; end + 8'h3e: begin outa = 10'h354; outb = 2'b01; outc = 1'b1; end + 8'h3f: begin outa = 10'h0db; outb = 2'b11; outc = 1'b0; end + 8'h40: begin outa = 10'h238; outb = 2'b01; outc = 1'b0; end + 8'h41: begin outa = 10'h12b; outb = 2'b01; outc = 1'b1; end + 8'h42: begin outa = 10'h1dc; outb = 2'b10; outc = 1'b0; end + 8'h43: begin outa = 10'h137; outb = 2'b01; outc = 1'b1; end + 8'h44: begin outa = 10'h1e2; outb = 2'b01; outc = 1'b1; end + 8'h45: begin outa = 10'h3d5; outb = 2'b11; outc = 1'b1; end + 8'h46: begin outa = 10'h30c; outb = 2'b11; outc = 1'b0; end + 8'h47: begin outa = 10'h298; outb = 2'b11; outc = 1'b0; end + 8'h48: begin outa = 10'h080; outb = 2'b00; outc = 1'b1; end + 8'h49: begin outa = 10'h35a; outb = 2'b11; outc = 1'b1; end + 8'h4a: begin outa = 10'h01b; outb = 2'b00; outc = 1'b0; end + 8'h4b: begin outa = 10'h0a3; outb = 2'b11; outc = 1'b0; end + 8'h4c: begin outa = 10'h0b3; outb = 2'b11; outc = 1'b1; end + 8'h4d: begin outa = 10'h17a; outb = 2'b00; outc = 1'b0; end + 8'h4e: begin outa = 10'h3ae; outb = 2'b11; outc = 1'b0; end + 8'h4f: begin outa = 10'h078; outb = 2'b11; outc = 1'b0; end + 8'h50: begin outa = 10'h322; outb = 2'b00; outc = 1'b1; end + 8'h51: begin outa = 10'h213; outb = 2'b11; outc = 1'b0; end + 8'h52: begin outa = 10'h11a; outb = 2'b11; outc = 1'b0; end + 8'h53: begin outa = 10'h1a7; outb = 2'b00; outc = 1'b0; end + 8'h54: begin outa = 10'h35a; outb = 2'b00; outc = 1'b1; end + 8'h55: begin outa = 10'h233; outb = 2'b00; outc = 1'b0; end + 8'h56: begin outa = 10'h01d; outb = 2'b01; outc = 1'b1; end + 8'h57: begin outa = 10'h2d5; outb = 2'b00; outc = 1'b0; end + 8'h58: begin outa = 10'h1a0; outb = 2'b00; outc = 1'b1; end + 8'h59: begin outa = 10'h3d0; outb = 2'b00; outc = 1'b1; end + 8'h5a: begin outa = 10'h181; outb = 2'b01; outc = 1'b1; end + 8'h5b: begin outa = 10'h219; outb = 2'b01; outc = 1'b1; end + 8'h5c: begin outa = 10'h26a; outb = 2'b01; outc = 1'b1; end + 8'h5d: begin outa = 10'h050; outb = 2'b10; outc = 1'b0; end + 8'h5e: begin outa = 10'h189; outb = 2'b10; outc = 1'b0; end + 8'h5f: begin outa = 10'h1eb; outb = 2'b01; outc = 1'b1; end + 8'h60: begin outa = 10'h224; outb = 2'b00; outc = 1'b1; end + 8'h61: begin outa = 10'h2fe; outb = 2'b00; outc = 1'b0; end + 8'h62: begin outa = 10'h0ae; outb = 2'b00; outc = 1'b1; end + 8'h63: begin outa = 10'h1cd; outb = 2'b00; outc = 1'b0; end + 8'h64: begin outa = 10'h273; outb = 2'b10; outc = 1'b1; end + 8'h65: begin outa = 10'h268; outb = 2'b10; outc = 1'b0; end + 8'h66: begin outa = 10'h111; outb = 2'b01; outc = 1'b0; end + 8'h67: begin outa = 10'h1f9; outb = 2'b00; outc = 1'b0; end + 8'h68: begin outa = 10'h232; outb = 2'b00; outc = 1'b1; end + 8'h69: begin outa = 10'h255; outb = 2'b11; outc = 1'b0; end + 8'h6a: begin outa = 10'h34c; outb = 2'b01; outc = 1'b1; end + 8'h6b: begin outa = 10'h049; outb = 2'b01; outc = 1'b1; end + 8'h6c: begin outa = 10'h197; outb = 2'b11; outc = 1'b0; end + 8'h6d: begin outa = 10'h0fe; outb = 2'b11; outc = 1'b0; end + 8'h6e: begin outa = 10'h253; outb = 2'b01; outc = 1'b1; end + 8'h6f: begin outa = 10'h2de; outb = 2'b11; outc = 1'b0; end + 8'h70: begin outa = 10'h13b; outb = 2'b10; outc = 1'b1; end + 8'h71: begin outa = 10'h040; outb = 2'b10; outc = 1'b0; end + 8'h72: begin outa = 10'h0b4; outb = 2'b00; outc = 1'b1; end + 8'h73: begin outa = 10'h233; outb = 2'b11; outc = 1'b1; end + 8'h74: begin outa = 10'h198; outb = 2'b00; outc = 1'b1; end + 8'h75: begin outa = 10'h018; outb = 2'b00; outc = 1'b1; end + 8'h76: begin outa = 10'h2f7; outb = 2'b00; outc = 1'b1; end + 8'h77: begin outa = 10'h134; outb = 2'b11; outc = 1'b0; end + 8'h78: begin outa = 10'h1ca; outb = 2'b10; outc = 1'b0; end + 8'h79: begin outa = 10'h286; outb = 2'b10; outc = 1'b1; end + 8'h7a: begin outa = 10'h0e6; outb = 2'b11; outc = 1'b1; end + 8'h7b: begin outa = 10'h064; outb = 2'b10; outc = 1'b1; end + 8'h7c: begin outa = 10'h257; outb = 2'b00; outc = 1'b1; end + 8'h7d: begin outa = 10'h31a; outb = 2'b10; outc = 1'b1; end + 8'h7e: begin outa = 10'h247; outb = 2'b01; outc = 1'b0; end + 8'h7f: begin outa = 10'h299; outb = 2'b00; outc = 1'b0; end + 8'h80: begin outa = 10'h02c; outb = 2'b00; outc = 1'b0; end + 8'h81: begin outa = 10'h2bb; outb = 2'b11; outc = 1'b0; end + 8'h82: begin outa = 10'h180; outb = 2'b10; outc = 1'b0; end + 8'h83: begin outa = 10'h245; outb = 2'b01; outc = 1'b1; end + 8'h84: begin outa = 10'h0da; outb = 2'b10; outc = 1'b0; end + 8'h85: begin outa = 10'h367; outb = 2'b10; outc = 1'b0; end + 8'h86: begin outa = 10'h304; outb = 2'b01; outc = 1'b0; end + 8'h87: begin outa = 10'h38b; outb = 2'b11; outc = 1'b0; end + 8'h88: begin outa = 10'h09f; outb = 2'b01; outc = 1'b0; end + 8'h89: begin outa = 10'h1f0; outb = 2'b10; outc = 1'b1; end + 8'h8a: begin outa = 10'h281; outb = 2'b10; outc = 1'b1; end + 8'h8b: begin outa = 10'h019; outb = 2'b00; outc = 1'b0; end + 8'h8c: begin outa = 10'h1f2; outb = 2'b10; outc = 1'b0; end + 8'h8d: begin outa = 10'h0b1; outb = 2'b01; outc = 1'b1; end + 8'h8e: begin outa = 10'h058; outb = 2'b01; outc = 1'b1; end + 8'h8f: begin outa = 10'h39b; outb = 2'b00; outc = 1'b1; end + 8'h90: begin outa = 10'h2ec; outb = 2'b10; outc = 1'b1; end + 8'h91: begin outa = 10'h250; outb = 2'b00; outc = 1'b1; end + 8'h92: begin outa = 10'h3f4; outb = 2'b10; outc = 1'b1; end + 8'h93: begin outa = 10'h057; outb = 2'b10; outc = 1'b1; end + 8'h94: begin outa = 10'h18f; outb = 2'b01; outc = 1'b1; end + 8'h95: begin outa = 10'h105; outb = 2'b01; outc = 1'b1; end + 8'h96: begin outa = 10'h1ae; outb = 2'b00; outc = 1'b1; end + 8'h97: begin outa = 10'h04e; outb = 2'b10; outc = 1'b0; end + 8'h98: begin outa = 10'h240; outb = 2'b11; outc = 1'b0; end + 8'h99: begin outa = 10'h3e4; outb = 2'b01; outc = 1'b0; end + 8'h9a: begin outa = 10'h3c6; outb = 2'b01; outc = 1'b0; end + 8'h9b: begin outa = 10'h109; outb = 2'b00; outc = 1'b1; end + 8'h9c: begin outa = 10'h073; outb = 2'b10; outc = 1'b1; end + 8'h9d: begin outa = 10'h19f; outb = 2'b01; outc = 1'b0; end + 8'h9e: begin outa = 10'h3b8; outb = 2'b01; outc = 1'b0; end + 8'h9f: begin outa = 10'h00e; outb = 2'b00; outc = 1'b1; end + 8'ha0: begin outa = 10'h1b3; outb = 2'b11; outc = 1'b1; end + 8'ha1: begin outa = 10'h2bd; outb = 2'b11; outc = 1'b0; end + 8'ha2: begin outa = 10'h324; outb = 2'b00; outc = 1'b1; end + 8'ha3: begin outa = 10'h343; outb = 2'b10; outc = 1'b0; end + 8'ha4: begin outa = 10'h1c9; outb = 2'b01; outc = 1'b0; end + 8'ha5: begin outa = 10'h185; outb = 2'b00; outc = 1'b1; end + 8'ha6: begin outa = 10'h37a; outb = 2'b00; outc = 1'b1; end + 8'ha7: begin outa = 10'h0e0; outb = 2'b01; outc = 1'b1; end + 8'ha8: begin outa = 10'h0a3; outb = 2'b10; outc = 1'b0; end + 8'ha9: begin outa = 10'h019; outb = 2'b11; outc = 1'b0; end + 8'haa: begin outa = 10'h099; outb = 2'b00; outc = 1'b1; end + 8'hab: begin outa = 10'h376; outb = 2'b01; outc = 1'b1; end + 8'hac: begin outa = 10'h077; outb = 2'b00; outc = 1'b1; end + 8'had: begin outa = 10'h2b1; outb = 2'b11; outc = 1'b1; end + 8'hae: begin outa = 10'h27f; outb = 2'b00; outc = 1'b0; end + 8'haf: begin outa = 10'h265; outb = 2'b11; outc = 1'b0; end + 8'hb0: begin outa = 10'h156; outb = 2'b10; outc = 1'b1; end + 8'hb1: begin outa = 10'h1ce; outb = 2'b00; outc = 1'b0; end + 8'hb2: begin outa = 10'h008; outb = 2'b01; outc = 1'b0; end + 8'hb3: begin outa = 10'h12e; outb = 2'b11; outc = 1'b1; end + 8'hb4: begin outa = 10'h199; outb = 2'b11; outc = 1'b0; end + 8'hb5: begin outa = 10'h330; outb = 2'b10; outc = 1'b0; end + 8'hb6: begin outa = 10'h1ab; outb = 2'b01; outc = 1'b1; end + 8'hb7: begin outa = 10'h3bd; outb = 2'b00; outc = 1'b0; end + 8'hb8: begin outa = 10'h0ca; outb = 2'b10; outc = 1'b0; end + 8'hb9: begin outa = 10'h367; outb = 2'b00; outc = 1'b0; end + 8'hba: begin outa = 10'h334; outb = 2'b00; outc = 1'b0; end + 8'hbb: begin outa = 10'h040; outb = 2'b00; outc = 1'b1; end + 8'hbc: begin outa = 10'h1a7; outb = 2'b10; outc = 1'b1; end + 8'hbd: begin outa = 10'h036; outb = 2'b11; outc = 1'b1; end + 8'hbe: begin outa = 10'h223; outb = 2'b11; outc = 1'b1; end + 8'hbf: begin outa = 10'h075; outb = 2'b01; outc = 1'b0; end + 8'hc0: begin outa = 10'h3c4; outb = 2'b00; outc = 1'b1; end + 8'hc1: begin outa = 10'h2cc; outb = 2'b01; outc = 1'b0; end + 8'hc2: begin outa = 10'h123; outb = 2'b01; outc = 1'b0; end + 8'hc3: begin outa = 10'h3fd; outb = 2'b01; outc = 1'b1; end + 8'hc4: begin outa = 10'h11e; outb = 2'b00; outc = 1'b0; end + 8'hc5: begin outa = 10'h27c; outb = 2'b11; outc = 1'b1; end + 8'hc6: begin outa = 10'h1e2; outb = 2'b11; outc = 1'b0; end + 8'hc7: begin outa = 10'h377; outb = 2'b11; outc = 1'b0; end + 8'hc8: begin outa = 10'h33a; outb = 2'b11; outc = 1'b0; end + 8'hc9: begin outa = 10'h32d; outb = 2'b11; outc = 1'b1; end + 8'hca: begin outa = 10'h014; outb = 2'b11; outc = 1'b0; end + 8'hcb: begin outa = 10'h332; outb = 2'b10; outc = 1'b0; end + 8'hcc: begin outa = 10'h359; outb = 2'b00; outc = 1'b0; end + 8'hcd: begin outa = 10'h0a4; outb = 2'b10; outc = 1'b1; end + 8'hce: begin outa = 10'h348; outb = 2'b00; outc = 1'b1; end + 8'hcf: begin outa = 10'h04b; outb = 2'b11; outc = 1'b1; end + 8'hd0: begin outa = 10'h147; outb = 2'b10; outc = 1'b1; end + 8'hd1: begin outa = 10'h026; outb = 2'b00; outc = 1'b1; end + 8'hd2: begin outa = 10'h103; outb = 2'b00; outc = 1'b0; end + 8'hd3: begin outa = 10'h106; outb = 2'b00; outc = 1'b1; end + 8'hd4: begin outa = 10'h35a; outb = 2'b00; outc = 1'b0; end + 8'hd5: begin outa = 10'h254; outb = 2'b01; outc = 1'b0; end + 8'hd6: begin outa = 10'h0cd; outb = 2'b01; outc = 1'b0; end + 8'hd7: begin outa = 10'h17c; outb = 2'b11; outc = 1'b1; end + 8'hd8: begin outa = 10'h37e; outb = 2'b10; outc = 1'b1; end + 8'hd9: begin outa = 10'h0a9; outb = 2'b11; outc = 1'b1; end + 8'hda: begin outa = 10'h0fe; outb = 2'b01; outc = 1'b0; end + 8'hdb: begin outa = 10'h3c0; outb = 2'b11; outc = 1'b1; end + 8'hdc: begin outa = 10'h1d9; outb = 2'b10; outc = 1'b1; end + 8'hdd: begin outa = 10'h10e; outb = 2'b00; outc = 1'b1; end + 8'hde: begin outa = 10'h394; outb = 2'b01; outc = 1'b0; end + 8'hdf: begin outa = 10'h316; outb = 2'b01; outc = 1'b0; end + 8'he0: begin outa = 10'h05b; outb = 2'b11; outc = 1'b0; end + 8'he1: begin outa = 10'h126; outb = 2'b01; outc = 1'b1; end + 8'he2: begin outa = 10'h369; outb = 2'b11; outc = 1'b0; end + 8'he3: begin outa = 10'h291; outb = 2'b10; outc = 1'b1; end + 8'he4: begin outa = 10'h2ca; outb = 2'b00; outc = 1'b1; end + 8'he5: begin outa = 10'h25b; outb = 2'b01; outc = 1'b1; end + 8'he6: begin outa = 10'h106; outb = 2'b00; outc = 1'b0; end + 8'he7: begin outa = 10'h172; outb = 2'b11; outc = 1'b1; end + 8'he8: begin outa = 10'h2f7; outb = 2'b00; outc = 1'b1; end + 8'he9: begin outa = 10'h2d3; outb = 2'b11; outc = 1'b1; end + 8'hea: begin outa = 10'h182; outb = 2'b00; outc = 1'b0; end + 8'heb: begin outa = 10'h327; outb = 2'b00; outc = 1'b1; end + 8'hec: begin outa = 10'h1d0; outb = 2'b10; outc = 1'b0; end + 8'hed: begin outa = 10'h204; outb = 2'b00; outc = 1'b1; end + 8'hee: begin outa = 10'h11f; outb = 2'b00; outc = 1'b1; end + 8'hef: begin outa = 10'h365; outb = 2'b11; outc = 1'b1; end + 8'hf0: begin outa = 10'h2c2; outb = 2'b01; outc = 1'b1; end + 8'hf1: begin outa = 10'h2b5; outb = 2'b10; outc = 1'b0; end + 8'hf2: begin outa = 10'h1f8; outb = 2'b10; outc = 1'b1; end + 8'hf3: begin outa = 10'h2a7; outb = 2'b01; outc = 1'b1; end + 8'hf4: begin outa = 10'h1be; outb = 2'b10; outc = 1'b1; end + 8'hf5: begin outa = 10'h25e; outb = 2'b10; outc = 1'b1; end + 8'hf6: begin outa = 10'h032; outb = 2'b10; outc = 1'b0; end + 8'hf7: begin outa = 10'h2ef; outb = 2'b00; outc = 1'b0; end + 8'hf8: begin outa = 10'h02f; outb = 2'b00; outc = 1'b1; end + 8'hf9: begin outa = 10'h201; outb = 2'b10; outc = 1'b0; end + 8'hfa: begin outa = 10'h054; outb = 2'b01; outc = 1'b1; end + 8'hfb: begin outa = 10'h013; outb = 2'b10; outc = 1'b0; end + 8'hfc: begin outa = 10'h249; outb = 2'b01; outc = 1'b0; end + 8'hfd: begin outa = 10'h09a; outb = 2'b10; outc = 1'b0; end + 8'hfe: begin outa = 10'h012; outb = 2'b00; outc = 1'b0; end + 8'hff: begin outa = 10'h114; outb = 2'b10; outc = 1'b1; end + endcase + end +endmodule + diff --git a/test_regress/t/t_case_huge_sub2.v b/test_regress/t/t_case_huge_sub2.v new file mode 100644 index 000000000..f743eee94 --- /dev/null +++ b/test_regress/t/t_case_huge_sub2.v @@ -0,0 +1,292 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t_case_huge_sub2 (/*AUTOARG*/ + // Outputs + outa, + // Inputs + index + ); + + input [9:0] index; + output [9:0] outa; + + // ============================= + /*AUTOREG*/ + // Beginning of automatic regs (for this module's undeclared outputs) + reg [9:0] outa; + // End of automatics + + // ============================= + // Created from perl + // for $i (0..1023) { printf "\t10'h%03x: begin outa = 10'h%03x; outb = 2'b%02b; outc = 1'b%d; end\n", $i, rand(1024),rand(4),rand(2); }; + + always @(/*AS*/index) begin + case (index[7:0]) +`ifdef verilator + 8'h00: begin outa = $c("0"); end // Makes whole table non-optimizable +`else + 8'h00: begin outa = 10'h0; end +`endif + 8'h01: begin outa = 10'h318; end + 8'h02: begin outa = 10'h29f; end + 8'h03: begin outa = 10'h392; end + 8'h04: begin outa = 10'h1ef; end + 8'h05: begin outa = 10'h06c; end + 8'h06: begin outa = 10'h29f; end + 8'h07: begin outa = 10'h29a; end + 8'h08: begin outa = 10'h3ce; end + 8'h09: begin outa = 10'h37c; end + 8'h0a: begin outa = 10'h058; end + 8'h0b: begin outa = 10'h3b2; end + 8'h0c: begin outa = 10'h36f; end + 8'h0d: begin outa = 10'h2c5; end + 8'h0e: begin outa = 10'h23a; end + 8'h0f: begin outa = 10'h222; end + 8'h10: begin outa = 10'h328; end + 8'h11: begin outa = 10'h3c3; end + 8'h12: begin outa = 10'h12c; end + 8'h13: begin outa = 10'h1d0; end + 8'h14: begin outa = 10'h3ff; end + 8'h15: begin outa = 10'h115; end + 8'h16: begin outa = 10'h3ba; end + 8'h17: begin outa = 10'h3ba; end + 8'h18: begin outa = 10'h10d; end + 8'h19: begin outa = 10'h13b; end + 8'h1a: begin outa = 10'h0a0; end + 8'h1b: begin outa = 10'h264; end + 8'h1c: begin outa = 10'h3a2; end + 8'h1d: begin outa = 10'h07c; end + 8'h1e: begin outa = 10'h291; end + 8'h1f: begin outa = 10'h1d1; end + 8'h20: begin outa = 10'h354; end + 8'h21: begin outa = 10'h0c0; end + 8'h22: begin outa = 10'h191; end + 8'h23: begin outa = 10'h379; end + 8'h24: begin outa = 10'h073; end + 8'h25: begin outa = 10'h2fd; end + 8'h26: begin outa = 10'h2e0; end + 8'h27: begin outa = 10'h337; end + 8'h28: begin outa = 10'h2c7; end + 8'h29: begin outa = 10'h19e; end + 8'h2a: begin outa = 10'h107; end + 8'h2b: begin outa = 10'h06a; end + 8'h2c: begin outa = 10'h1c7; end + 8'h2d: begin outa = 10'h107; end + 8'h2e: begin outa = 10'h0cf; end + 8'h2f: begin outa = 10'h009; end + 8'h30: begin outa = 10'h09d; end + 8'h31: begin outa = 10'h28e; end + 8'h32: begin outa = 10'h010; end + 8'h33: begin outa = 10'h1e0; end + 8'h34: begin outa = 10'h079; end + 8'h35: begin outa = 10'h13e; end + 8'h36: begin outa = 10'h282; end + 8'h37: begin outa = 10'h21c; end + 8'h38: begin outa = 10'h148; end + 8'h39: begin outa = 10'h3c0; end + 8'h3a: begin outa = 10'h176; end + 8'h3b: begin outa = 10'h3fc; end + 8'h3c: begin outa = 10'h295; end + 8'h3d: begin outa = 10'h113; end + 8'h3e: begin outa = 10'h354; end + 8'h3f: begin outa = 10'h0db; end + 8'h40: begin outa = 10'h238; end + 8'h41: begin outa = 10'h12b; end + 8'h42: begin outa = 10'h1dc; end + 8'h43: begin outa = 10'h137; end + 8'h44: begin outa = 10'h1e2; end + 8'h45: begin outa = 10'h3d5; end + 8'h46: begin outa = 10'h30c; end + 8'h47: begin outa = 10'h298; end + 8'h48: begin outa = 10'h080; end + 8'h49: begin outa = 10'h35a; end + 8'h4a: begin outa = 10'h01b; end + 8'h4b: begin outa = 10'h0a3; end + 8'h4c: begin outa = 10'h0b3; end + 8'h4d: begin outa = 10'h17a; end + 8'h4e: begin outa = 10'h3ae; end + 8'h4f: begin outa = 10'h078; end + 8'h50: begin outa = 10'h322; end + 8'h51: begin outa = 10'h213; end + 8'h52: begin outa = 10'h11a; end + 8'h53: begin outa = 10'h1a7; end + 8'h54: begin outa = 10'h35a; end + 8'h55: begin outa = 10'h233; end + 8'h56: begin outa = 10'h01d; end + 8'h57: begin outa = 10'h2d5; end + 8'h58: begin outa = 10'h1a0; end + 8'h59: begin outa = 10'h3d0; end + 8'h5a: begin outa = 10'h181; end + 8'h5b: begin outa = 10'h219; end + 8'h5c: begin outa = 10'h26a; end + 8'h5d: begin outa = 10'h050; end + 8'h5e: begin outa = 10'h189; end + 8'h5f: begin outa = 10'h1eb; end + 8'h60: begin outa = 10'h224; end + 8'h61: begin outa = 10'h2fe; end + 8'h62: begin outa = 10'h0ae; end + 8'h63: begin outa = 10'h1cd; end + 8'h64: begin outa = 10'h273; end + 8'h65: begin outa = 10'h268; end + 8'h66: begin outa = 10'h111; end + 8'h67: begin outa = 10'h1f9; end + 8'h68: begin outa = 10'h232; end + 8'h69: begin outa = 10'h255; end + 8'h6a: begin outa = 10'h34c; end + 8'h6b: begin outa = 10'h049; end + 8'h6c: begin outa = 10'h197; end + 8'h6d: begin outa = 10'h0fe; end + 8'h6e: begin outa = 10'h253; end + 8'h6f: begin outa = 10'h2de; end + 8'h70: begin outa = 10'h13b; end + 8'h71: begin outa = 10'h040; end + 8'h72: begin outa = 10'h0b4; end + 8'h73: begin outa = 10'h233; end + 8'h74: begin outa = 10'h198; end + 8'h75: begin outa = 10'h018; end + 8'h76: begin outa = 10'h2f7; end + 8'h77: begin outa = 10'h134; end + 8'h78: begin outa = 10'h1ca; end + 8'h79: begin outa = 10'h286; end + 8'h7a: begin outa = 10'h0e6; end + 8'h7b: begin outa = 10'h064; end + 8'h7c: begin outa = 10'h257; end + 8'h7d: begin outa = 10'h31a; end + 8'h7e: begin outa = 10'h247; end + 8'h7f: begin outa = 10'h299; end + 8'h80: begin outa = 10'h02c; end + 8'h81: begin outa = 10'h2bb; end + 8'h82: begin outa = 10'h180; end + 8'h83: begin outa = 10'h245; end + 8'h84: begin outa = 10'h0da; end + 8'h85: begin outa = 10'h367; end + 8'h86: begin outa = 10'h304; end + 8'h87: begin outa = 10'h38b; end + 8'h88: begin outa = 10'h09f; end + 8'h89: begin outa = 10'h1f0; end + 8'h8a: begin outa = 10'h281; end + 8'h8b: begin outa = 10'h019; end + 8'h8c: begin outa = 10'h1f2; end + 8'h8d: begin outa = 10'h0b1; end + 8'h8e: begin outa = 10'h058; end + 8'h8f: begin outa = 10'h39b; end + 8'h90: begin outa = 10'h2ec; end + 8'h91: begin outa = 10'h250; end + 8'h92: begin outa = 10'h3f4; end + 8'h93: begin outa = 10'h057; end + 8'h94: begin outa = 10'h18f; end + 8'h95: begin outa = 10'h105; end + 8'h96: begin outa = 10'h1ae; end + 8'h97: begin outa = 10'h04e; end + 8'h98: begin outa = 10'h240; end + 8'h99: begin outa = 10'h3e4; end + 8'h9a: begin outa = 10'h3c6; end + 8'h9b: begin outa = 10'h109; end + 8'h9c: begin outa = 10'h073; end + 8'h9d: begin outa = 10'h19f; end + 8'h9e: begin outa = 10'h3b8; end + 8'h9f: begin outa = 10'h00e; end + 8'ha0: begin outa = 10'h1b3; end + 8'ha1: begin outa = 10'h2bd; end + 8'ha2: begin outa = 10'h324; end + 8'ha3: begin outa = 10'h343; end + 8'ha4: begin outa = 10'h1c9; end + 8'ha5: begin outa = 10'h185; end + 8'ha6: begin outa = 10'h37a; end + 8'ha7: begin outa = 10'h0e0; end + 8'ha8: begin outa = 10'h0a3; end + 8'ha9: begin outa = 10'h019; end + 8'haa: begin outa = 10'h099; end + 8'hab: begin outa = 10'h376; end + 8'hac: begin outa = 10'h077; end + 8'had: begin outa = 10'h2b1; end + 8'hae: begin outa = 10'h27f; end + 8'haf: begin outa = 10'h265; end + 8'hb0: begin outa = 10'h156; end + 8'hb1: begin outa = 10'h1ce; end + 8'hb2: begin outa = 10'h008; end + 8'hb3: begin outa = 10'h12e; end + 8'hb4: begin outa = 10'h199; end + 8'hb5: begin outa = 10'h330; end + 8'hb6: begin outa = 10'h1ab; end + 8'hb7: begin outa = 10'h3bd; end + 8'hb8: begin outa = 10'h0ca; end + 8'hb9: begin outa = 10'h367; end + 8'hba: begin outa = 10'h334; end + 8'hbb: begin outa = 10'h040; end + 8'hbc: begin outa = 10'h1a7; end + 8'hbd: begin outa = 10'h036; end + 8'hbe: begin outa = 10'h223; end + 8'hbf: begin outa = 10'h075; end + 8'hc0: begin outa = 10'h3c4; end + 8'hc1: begin outa = 10'h2cc; end + 8'hc2: begin outa = 10'h123; end + 8'hc3: begin outa = 10'h3fd; end + 8'hc4: begin outa = 10'h11e; end + 8'hc5: begin outa = 10'h27c; end + 8'hc6: begin outa = 10'h1e2; end + 8'hc7: begin outa = 10'h377; end + 8'hc8: begin outa = 10'h33a; end + 8'hc9: begin outa = 10'h32d; end + 8'hca: begin outa = 10'h014; end + 8'hcb: begin outa = 10'h332; end + 8'hcc: begin outa = 10'h359; end + 8'hcd: begin outa = 10'h0a4; end + 8'hce: begin outa = 10'h348; end + 8'hcf: begin outa = 10'h04b; end + 8'hd0: begin outa = 10'h147; end + 8'hd1: begin outa = 10'h026; end + 8'hd2: begin outa = 10'h103; end + 8'hd3: begin outa = 10'h106; end + 8'hd4: begin outa = 10'h35a; end + 8'hd5: begin outa = 10'h254; end + 8'hd6: begin outa = 10'h0cd; end + 8'hd7: begin outa = 10'h17c; end + 8'hd8: begin outa = 10'h37e; end + 8'hd9: begin outa = 10'h0a9; end + 8'hda: begin outa = 10'h0fe; end + 8'hdb: begin outa = 10'h3c0; end + 8'hdc: begin outa = 10'h1d9; end + 8'hdd: begin outa = 10'h10e; end + 8'hde: begin outa = 10'h394; end + 8'hdf: begin outa = 10'h316; end + 8'he0: begin outa = 10'h05b; end + 8'he1: begin outa = 10'h126; end + 8'he2: begin outa = 10'h369; end + 8'he3: begin outa = 10'h291; end + 8'he4: begin outa = 10'h2ca; end + 8'he5: begin outa = 10'h25b; end + 8'he6: begin outa = 10'h106; end + 8'he7: begin outa = 10'h172; end + 8'he8: begin outa = 10'h2f7; end + 8'he9: begin outa = 10'h2d3; end + 8'hea: begin outa = 10'h182; end + 8'heb: begin outa = 10'h327; end + 8'hec: begin outa = 10'h1d0; end + 8'hed: begin outa = 10'h204; end + 8'hee: begin outa = 10'h11f; end + 8'hef: begin outa = 10'h365; end + 8'hf0: begin outa = 10'h2c2; end + 8'hf1: begin outa = 10'h2b5; end + 8'hf2: begin outa = 10'h1f8; end + 8'hf3: begin outa = 10'h2a7; end + 8'hf4: begin outa = 10'h1be; end + 8'hf5: begin outa = 10'h25e; end + 8'hf6: begin outa = 10'h032; end + 8'hf7: begin outa = 10'h2ef; end + 8'hf8: begin outa = 10'h02f; end + 8'hf9: begin outa = 10'h201; end + 8'hfa: begin outa = 10'h054; end + 8'hfb: begin outa = 10'h013; end + 8'hfc: begin outa = 10'h249; end + 8'hfd: begin outa = 10'h09a; end + 8'hfe: begin outa = 10'h012; end + 8'hff: begin outa = 10'h114; end + endcase + end +endmodule + diff --git a/test_regress/t/t_case_huge_sub3.v b/test_regress/t/t_case_huge_sub3.v new file mode 100644 index 000000000..9455cada1 --- /dev/null +++ b/test_regress/t/t_case_huge_sub3.v @@ -0,0 +1,293 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t_case_huge_sub3 (/*AUTOARG*/ + // Outputs + outr, + // Inputs + clk, index + ); + + input clk; + input [9:0] index; + output [3:0] outr; + + // ============================= + /*AUTOREG*/ + // Beginning of automatic regs (for this module's undeclared outputs) + reg [3:0] outr; + // End of automatics + + // ============================= + // Created from perl + //for $i (0..255) { $r=rand(4); printf "\t8'h%02x: begin outr <= outr^index[8:5]^4'h%01x; end\n", $i, + //rand(256); }; + + // Reset cheating + initial outr = 4'b0; + + always @(posedge clk) begin + case (index[7:0]) + 8'h00: begin outr <= 4'h0; end + 8'h01: begin /*No Change*/ end + 8'h02: begin outr <= outr^index[8:5]^4'ha; end + 8'h03: begin outr <= outr^index[8:5]^4'h4; end + 8'h04: begin outr <= outr^index[8:5]^4'hd; end + 8'h05: begin outr <= outr^index[8:5]^4'h1; end + 8'h06: begin outr <= outr^index[8:5]^4'hf; end + 8'h07: begin outr <= outr^index[8:5]^4'he; end + 8'h08: begin outr <= outr^index[8:5]^4'h0; end + 8'h09: begin outr <= outr^index[8:5]^4'h4; end + 8'h0a: begin outr <= outr^index[8:5]^4'h5; end + 8'h0b: begin outr <= outr^index[8:5]^4'ha; end + 8'h0c: begin outr <= outr^index[8:5]^4'h2; end + 8'h0d: begin outr <= outr^index[8:5]^4'hf; end + 8'h0e: begin outr <= outr^index[8:5]^4'h5; end + 8'h0f: begin outr <= outr^index[8:5]^4'h0; end + 8'h10: begin outr <= outr^index[8:5]^4'h3; end + 8'h11: begin outr <= outr^index[8:5]^4'hb; end + 8'h12: begin outr <= outr^index[8:5]^4'h0; end + 8'h13: begin outr <= outr^index[8:5]^4'hf; end + 8'h14: begin outr <= outr^index[8:5]^4'h3; end + 8'h15: begin outr <= outr^index[8:5]^4'h5; end + 8'h16: begin outr <= outr^index[8:5]^4'h7; end + 8'h17: begin outr <= outr^index[8:5]^4'h2; end + 8'h18: begin outr <= outr^index[8:5]^4'h3; end + 8'h19: begin outr <= outr^index[8:5]^4'hb; end + 8'h1a: begin outr <= outr^index[8:5]^4'h5; end + 8'h1b: begin outr <= outr^index[8:5]^4'h4; end + 8'h1c: begin outr <= outr^index[8:5]^4'h2; end + 8'h1d: begin outr <= outr^index[8:5]^4'hf; end + 8'h1e: begin outr <= outr^index[8:5]^4'h0; end + 8'h1f: begin outr <= outr^index[8:5]^4'h4; end + 8'h20: begin outr <= outr^index[8:5]^4'h6; end + 8'h21: begin outr <= outr^index[8:5]^4'ha; end + 8'h22: begin outr <= outr^index[8:5]^4'h6; end + 8'h23: begin outr <= outr^index[8:5]^4'hb; end + 8'h24: begin outr <= outr^index[8:5]^4'ha; end + 8'h25: begin outr <= outr^index[8:5]^4'he; end + 8'h26: begin outr <= outr^index[8:5]^4'h7; end + 8'h27: begin outr <= outr^index[8:5]^4'ha; end + 8'h28: begin outr <= outr^index[8:5]^4'h3; end + 8'h29: begin outr <= outr^index[8:5]^4'h8; end + 8'h2a: begin outr <= outr^index[8:5]^4'h1; end + 8'h2b: begin outr <= outr^index[8:5]^4'h8; end + 8'h2c: begin outr <= outr^index[8:5]^4'h4; end + 8'h2d: begin outr <= outr^index[8:5]^4'h4; end + 8'h2e: begin outr <= outr^index[8:5]^4'he; end + 8'h2f: begin outr <= outr^index[8:5]^4'h8; end + 8'h30: begin outr <= outr^index[8:5]^4'ha; end + 8'h31: begin outr <= outr^index[8:5]^4'h7; end + 8'h32: begin outr <= outr^index[8:5]^4'h0; end + 8'h33: begin outr <= outr^index[8:5]^4'h3; end + 8'h34: begin outr <= outr^index[8:5]^4'h1; end + 8'h35: begin outr <= outr^index[8:5]^4'h3; end + 8'h36: begin outr <= outr^index[8:5]^4'h4; end + 8'h37: begin outr <= outr^index[8:5]^4'h6; end + 8'h38: begin outr <= outr^index[8:5]^4'h4; end + 8'h39: begin outr <= outr^index[8:5]^4'hb; end + 8'h3a: begin outr <= outr^index[8:5]^4'h7; end + 8'h3b: begin outr <= outr^index[8:5]^4'h1; end + 8'h3c: begin outr <= outr^index[8:5]^4'h2; end + 8'h3d: begin outr <= outr^index[8:5]^4'h0; end + 8'h3e: begin outr <= outr^index[8:5]^4'h2; end + 8'h3f: begin outr <= outr^index[8:5]^4'ha; end + 8'h40: begin outr <= outr^index[8:5]^4'h7; end + 8'h41: begin outr <= outr^index[8:5]^4'h5; end + 8'h42: begin outr <= outr^index[8:5]^4'h5; end + 8'h43: begin outr <= outr^index[8:5]^4'h4; end + 8'h44: begin outr <= outr^index[8:5]^4'h8; end + 8'h45: begin outr <= outr^index[8:5]^4'h5; end + 8'h46: begin outr <= outr^index[8:5]^4'hf; end + 8'h47: begin outr <= outr^index[8:5]^4'h6; end + 8'h48: begin outr <= outr^index[8:5]^4'h7; end + 8'h49: begin outr <= outr^index[8:5]^4'h4; end + 8'h4a: begin outr <= outr^index[8:5]^4'ha; end + 8'h4b: begin outr <= outr^index[8:5]^4'hd; end + 8'h4c: begin outr <= outr^index[8:5]^4'hb; end + 8'h4d: begin outr <= outr^index[8:5]^4'hf; end + 8'h4e: begin outr <= outr^index[8:5]^4'hd; end + 8'h4f: begin outr <= outr^index[8:5]^4'h7; end + 8'h50: begin outr <= outr^index[8:5]^4'h9; end + 8'h51: begin outr <= outr^index[8:5]^4'ha; end + 8'h52: begin outr <= outr^index[8:5]^4'hf; end + 8'h53: begin outr <= outr^index[8:5]^4'h3; end + 8'h54: begin outr <= outr^index[8:5]^4'h1; end + 8'h55: begin outr <= outr^index[8:5]^4'h0; end + 8'h56: begin outr <= outr^index[8:5]^4'h2; end + 8'h57: begin outr <= outr^index[8:5]^4'h9; end + 8'h58: begin outr <= outr^index[8:5]^4'h2; end + 8'h59: begin outr <= outr^index[8:5]^4'h4; end + 8'h5a: begin outr <= outr^index[8:5]^4'hc; end + 8'h5b: begin outr <= outr^index[8:5]^4'hd; end + 8'h5c: begin outr <= outr^index[8:5]^4'h3; end + 8'h5d: begin outr <= outr^index[8:5]^4'hb; end + 8'h5e: begin outr <= outr^index[8:5]^4'hd; end + 8'h5f: begin outr <= outr^index[8:5]^4'h7; end + 8'h60: begin outr <= outr^index[8:5]^4'h7; end + 8'h61: begin outr <= outr^index[8:5]^4'h3; end + 8'h62: begin outr <= outr^index[8:5]^4'h3; end + 8'h63: begin outr <= outr^index[8:5]^4'hb; end + 8'h64: begin outr <= outr^index[8:5]^4'h9; end + 8'h65: begin outr <= outr^index[8:5]^4'h4; end + 8'h66: begin outr <= outr^index[8:5]^4'h3; end + 8'h67: begin outr <= outr^index[8:5]^4'h6; end + 8'h68: begin outr <= outr^index[8:5]^4'h7; end + 8'h69: begin outr <= outr^index[8:5]^4'h7; end + 8'h6a: begin outr <= outr^index[8:5]^4'hf; end + 8'h6b: begin outr <= outr^index[8:5]^4'h6; end + 8'h6c: begin outr <= outr^index[8:5]^4'h8; end + 8'h6d: begin outr <= outr^index[8:5]^4'he; end + 8'h6e: begin outr <= outr^index[8:5]^4'h4; end + 8'h6f: begin outr <= outr^index[8:5]^4'h6; end + 8'h70: begin outr <= outr^index[8:5]^4'hc; end + 8'h71: begin outr <= outr^index[8:5]^4'h9; end + 8'h72: begin outr <= outr^index[8:5]^4'h5; end + 8'h73: begin outr <= outr^index[8:5]^4'ha; end + 8'h74: begin outr <= outr^index[8:5]^4'h7; end + 8'h75: begin outr <= outr^index[8:5]^4'h0; end + 8'h76: begin outr <= outr^index[8:5]^4'h1; end + 8'h77: begin outr <= outr^index[8:5]^4'he; end + 8'h78: begin outr <= outr^index[8:5]^4'ha; end + 8'h79: begin outr <= outr^index[8:5]^4'h7; end + 8'h7a: begin outr <= outr^index[8:5]^4'hf; end + 8'h7b: begin outr <= outr^index[8:5]^4'he; end + 8'h7c: begin outr <= outr^index[8:5]^4'h6; end + 8'h7d: begin outr <= outr^index[8:5]^4'hc; end + 8'h7e: begin outr <= outr^index[8:5]^4'hc; end + 8'h7f: begin outr <= outr^index[8:5]^4'h0; end + 8'h80: begin outr <= outr^index[8:5]^4'h0; end + 8'h81: begin outr <= outr^index[8:5]^4'hd; end + 8'h82: begin outr <= outr^index[8:5]^4'hb; end + 8'h83: begin outr <= outr^index[8:5]^4'hc; end + 8'h84: begin outr <= outr^index[8:5]^4'h2; end + 8'h85: begin outr <= outr^index[8:5]^4'h8; end + 8'h86: begin outr <= outr^index[8:5]^4'h3; end + 8'h87: begin outr <= outr^index[8:5]^4'ha; end + 8'h88: begin outr <= outr^index[8:5]^4'he; end + 8'h89: begin outr <= outr^index[8:5]^4'h9; end + 8'h8a: begin outr <= outr^index[8:5]^4'h1; end + 8'h8b: begin outr <= outr^index[8:5]^4'h1; end + 8'h8c: begin outr <= outr^index[8:5]^4'hc; end + 8'h8d: begin outr <= outr^index[8:5]^4'h2; end + 8'h8e: begin outr <= outr^index[8:5]^4'h2; end + 8'h8f: begin outr <= outr^index[8:5]^4'hd; end + 8'h90: begin outr <= outr^index[8:5]^4'h0; end + 8'h91: begin outr <= outr^index[8:5]^4'h6; end + 8'h92: begin outr <= outr^index[8:5]^4'h7; end + 8'h93: begin outr <= outr^index[8:5]^4'hc; end + 8'h94: begin outr <= outr^index[8:5]^4'hb; end + 8'h95: begin outr <= outr^index[8:5]^4'h3; end + 8'h96: begin outr <= outr^index[8:5]^4'h0; end + 8'h97: begin outr <= outr^index[8:5]^4'hc; end + 8'h98: begin outr <= outr^index[8:5]^4'hc; end + 8'h99: begin outr <= outr^index[8:5]^4'hb; end + 8'h9a: begin outr <= outr^index[8:5]^4'h6; end + 8'h9b: begin outr <= outr^index[8:5]^4'h5; end + 8'h9c: begin outr <= outr^index[8:5]^4'h5; end + 8'h9d: begin outr <= outr^index[8:5]^4'h4; end + 8'h9e: begin outr <= outr^index[8:5]^4'h7; end + 8'h9f: begin outr <= outr^index[8:5]^4'he; end + 8'ha0: begin outr <= outr^index[8:5]^4'hc; end + 8'ha1: begin outr <= outr^index[8:5]^4'hc; end + 8'ha2: begin outr <= outr^index[8:5]^4'h0; end + 8'ha3: begin outr <= outr^index[8:5]^4'h1; end + 8'ha4: begin outr <= outr^index[8:5]^4'hd; end + 8'ha5: begin outr <= outr^index[8:5]^4'h3; end + 8'ha6: begin outr <= outr^index[8:5]^4'hc; end + 8'ha7: begin outr <= outr^index[8:5]^4'h2; end + 8'ha8: begin outr <= outr^index[8:5]^4'h3; end + 8'ha9: begin outr <= outr^index[8:5]^4'hd; end + 8'haa: begin outr <= outr^index[8:5]^4'h5; end + 8'hab: begin outr <= outr^index[8:5]^4'hb; end + 8'hac: begin outr <= outr^index[8:5]^4'he; end + 8'had: begin outr <= outr^index[8:5]^4'h0; end + 8'hae: begin outr <= outr^index[8:5]^4'hf; end + 8'haf: begin outr <= outr^index[8:5]^4'h9; end + 8'hb0: begin outr <= outr^index[8:5]^4'hf; end + 8'hb1: begin outr <= outr^index[8:5]^4'h7; end + 8'hb2: begin outr <= outr^index[8:5]^4'h9; end + 8'hb3: begin outr <= outr^index[8:5]^4'hf; end + 8'hb4: begin outr <= outr^index[8:5]^4'he; end + 8'hb5: begin outr <= outr^index[8:5]^4'h3; end + 8'hb6: begin outr <= outr^index[8:5]^4'he; end + 8'hb7: begin outr <= outr^index[8:5]^4'h8; end + 8'hb8: begin outr <= outr^index[8:5]^4'hf; end + 8'hb9: begin outr <= outr^index[8:5]^4'hd; end + 8'hba: begin outr <= outr^index[8:5]^4'h3; end + 8'hbb: begin outr <= outr^index[8:5]^4'h5; end + 8'hbc: begin outr <= outr^index[8:5]^4'hd; end + 8'hbd: begin outr <= outr^index[8:5]^4'ha; end + 8'hbe: begin outr <= outr^index[8:5]^4'h7; end + 8'hbf: begin outr <= outr^index[8:5]^4'he; end + 8'hc0: begin outr <= outr^index[8:5]^4'h2; end + 8'hc1: begin outr <= outr^index[8:5]^4'he; end + 8'hc2: begin outr <= outr^index[8:5]^4'h9; end + 8'hc3: begin outr <= outr^index[8:5]^4'hb; end + 8'hc4: begin outr <= outr^index[8:5]^4'h0; end + 8'hc5: begin outr <= outr^index[8:5]^4'h5; end + 8'hc6: begin outr <= outr^index[8:5]^4'h9; end + 8'hc7: begin outr <= outr^index[8:5]^4'h6; end + 8'hc8: begin outr <= outr^index[8:5]^4'ha; end + 8'hc9: begin outr <= outr^index[8:5]^4'hf; end + 8'hca: begin outr <= outr^index[8:5]^4'h3; end + 8'hcb: begin outr <= outr^index[8:5]^4'hb; end + 8'hcc: begin outr <= outr^index[8:5]^4'he; end + 8'hcd: begin outr <= outr^index[8:5]^4'h2; end + 8'hce: begin outr <= outr^index[8:5]^4'h5; end + 8'hcf: begin outr <= outr^index[8:5]^4'hf; end + 8'hd0: begin outr <= outr^index[8:5]^4'h2; end + 8'hd1: begin outr <= outr^index[8:5]^4'h9; end + 8'hd2: begin outr <= outr^index[8:5]^4'hb; end + 8'hd3: begin outr <= outr^index[8:5]^4'h8; end + 8'hd4: begin outr <= outr^index[8:5]^4'h0; end + 8'hd5: begin outr <= outr^index[8:5]^4'h2; end + 8'hd6: begin outr <= outr^index[8:5]^4'hb; end + 8'hd7: begin outr <= outr^index[8:5]^4'h2; end + 8'hd8: begin outr <= outr^index[8:5]^4'ha; end + 8'hd9: begin outr <= outr^index[8:5]^4'hf; end + 8'hda: begin outr <= outr^index[8:5]^4'h8; end + 8'hdb: begin outr <= outr^index[8:5]^4'h4; end + 8'hdc: begin outr <= outr^index[8:5]^4'he; end + 8'hdd: begin outr <= outr^index[8:5]^4'h6; end + 8'hde: begin outr <= outr^index[8:5]^4'h9; end + 8'hdf: begin outr <= outr^index[8:5]^4'h9; end + 8'he0: begin outr <= outr^index[8:5]^4'h7; end + 8'he1: begin outr <= outr^index[8:5]^4'h0; end + 8'he2: begin outr <= outr^index[8:5]^4'h9; end + 8'he3: begin outr <= outr^index[8:5]^4'h3; end + 8'he4: begin outr <= outr^index[8:5]^4'h2; end + 8'he5: begin outr <= outr^index[8:5]^4'h4; end + 8'he6: begin outr <= outr^index[8:5]^4'h5; end + 8'he7: begin outr <= outr^index[8:5]^4'h5; end + 8'he8: begin outr <= outr^index[8:5]^4'hf; end + 8'he9: begin outr <= outr^index[8:5]^4'ha; end + 8'hea: begin outr <= outr^index[8:5]^4'hc; end + 8'heb: begin outr <= outr^index[8:5]^4'hd; end + 8'hec: begin outr <= outr^index[8:5]^4'h1; end + 8'hed: begin outr <= outr^index[8:5]^4'h5; end + 8'hee: begin outr <= outr^index[8:5]^4'h9; end + 8'hef: begin outr <= outr^index[8:5]^4'h0; end + 8'hf0: begin outr <= outr^index[8:5]^4'hd; end + 8'hf1: begin outr <= outr^index[8:5]^4'hf; end + 8'hf2: begin outr <= outr^index[8:5]^4'h4; end + 8'hf3: begin outr <= outr^index[8:5]^4'ha; end + 8'hf4: begin outr <= outr^index[8:5]^4'h8; end + 8'hf5: begin outr <= outr^index[8:5]^4'he; end + 8'hf6: begin outr <= outr^index[8:5]^4'he; end + 8'hf7: begin outr <= outr^index[8:5]^4'h1; end + 8'hf8: begin outr <= outr^index[8:5]^4'h6; end + 8'hf9: begin outr <= outr^index[8:5]^4'h0; end + 8'hfa: begin outr <= outr^index[8:5]^4'h5; end + 8'hfb: begin outr <= outr^index[8:5]^4'h1; end + 8'hfc: begin outr <= outr^index[8:5]^4'h8; end + 8'hfd: begin outr <= outr^index[8:5]^4'h6; end + 8'hfe: begin outr <= outr^index[8:5]^4'h1; end + default: begin outr <= outr^index[8:5]^4'h6; end + endcase + end +endmodule + diff --git a/test_regress/t/t_case_huge_sub4.v b/test_regress/t/t_case_huge_sub4.v new file mode 100644 index 000000000..e1e4d9909 --- /dev/null +++ b/test_regress/t/t_case_huge_sub4.v @@ -0,0 +1,64 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t_case_huge_sub4 (/*AUTOARG*/ + // Outputs + outq, + // Inputs + index + ); + + input [7:0] index; + output [9:0] outq; + + // ============================= + /*AUTOREG*/ + // Beginning of automatic regs (for this module's undeclared outputs) + reg [9:0] outq; + // End of automatics + + // ============================= + always @(/*AS*/index) begin + case (index) + // default below: no change + 8'h00: begin outq = 10'h001; end + 8'he0: begin outq = 10'h05b; end + 8'he1: begin outq = 10'h126; end + 8'he2: begin outq = 10'h369; end + 8'he3: begin outq = 10'h291; end + 8'he4: begin outq = 10'h2ca; end + 8'he5: begin outq = 10'h25b; end + 8'he6: begin outq = 10'h106; end + 8'he7: begin outq = 10'h172; end + 8'he8: begin outq = 10'h2f7; end + 8'he9: begin outq = 10'h2d3; end + 8'hea: begin outq = 10'h182; end + 8'heb: begin outq = 10'h327; end + 8'hec: begin outq = 10'h1d0; end + 8'hed: begin outq = 10'h204; end + 8'hee: begin outq = 10'h11f; end + 8'hef: begin outq = 10'h365; end + 8'hf0: begin outq = 10'h2c2; end + 8'hf1: begin outq = 10'h2b5; end + 8'hf2: begin outq = 10'h1f8; end + 8'hf3: begin outq = 10'h2a7; end + 8'hf4: begin outq = 10'h1be; end + 8'hf5: begin outq = 10'h25e; end + 8'hf6: begin outq = 10'h032; end + 8'hf7: begin outq = 10'h2ef; end + 8'hf8: begin outq = 10'h02f; end + 8'hf9: begin outq = 10'h201; end + 8'hfa: begin outq = 10'h054; end + 8'hfb: begin outq = 10'h013; end + 8'hfc: begin outq = 10'h249; end + 8'hfd: begin outq = 10'h09a; end + 8'hfe: begin outq = 10'h012; end + 8'hff: begin outq = 10'h114; end + default: ; // No change + endcase + end +endmodule + diff --git a/test_regress/t/t_case_itemwidth.pl b/test_regress/t/t_case_itemwidth.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_case_itemwidth.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_case_itemwidth.v b/test_regress/t/t_case_itemwidth.v new file mode 100644 index 000000000..d7e0d004e --- /dev/null +++ b/test_regress/t/t_case_itemwidth.v @@ -0,0 +1,124 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + // Some inputs we'll set to random values + reg [6:0] addr; + reg [6:0] e0; + reg [5:0] e1; + reg [5:0] e2; + + wire [7:0] data; + reg [2:0] wrapcheck_a; + reg [2:0] wrapcheck_b; + + test test (/*AUTOINST*/ + // Outputs + .data (data[7:0]), + // Inputs + .addr (addr[6:0]), + .e0 (e0[6:0]), + .e1 (e1[5:0]), + .e2 (e2[5:0])); + + always @(/*AS*/addr) begin + case(addr[2:0]) + 3'd0+3'd0: wrapcheck_a = 3'h0; + 3'd0+3'd1: wrapcheck_a = 3'h1; + 3'd0+3'd2: wrapcheck_a = 3'h2; + 3'd0+3'd3: wrapcheck_a = 3'h3; + default: wrapcheck_a = 3'h4; + endcase + + case(addr[2:0]) + 3'd0+0: wrapcheck_b = 3'h0; + 3'd1+1: wrapcheck_b = 3'h1; + 3'd2+2: wrapcheck_b = 3'h2; + 3'd3+3: wrapcheck_b = 3'h3; + default: wrapcheck_b = 3'h4; + endcase + end + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + //$write("%d %x %x %x\n", cyc, data, wrapcheck_a, wrapcheck_b); + if (cyc==1) begin + addr <= 7'h28; + e0 <= 7'h11; + e1 <= 6'h02; + e2 <= 6'h03; + end + if (cyc==2) begin + addr <= 7'h2b; + if (data != 8'h11) $stop; + end + if (cyc==3) begin + addr <= 7'h2c; + if (data != 8'h03) $stop; + if (wrapcheck_a != 3'h3) $stop; + if (wrapcheck_b != 3'h4) $stop; + end + if (cyc==4) begin + addr <= 7'h0; + if (data != 8'h00) $stop; + if (wrapcheck_a != 3'h4) $stop; + if (wrapcheck_b != 3'h2) $stop; + end + if (cyc==5) begin + if (data != 8'h00) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +/* verilator lint_off WIDTH */ +`define AI 7'h28 + +module test (/*AUTOARG*/ + // Outputs + data, + // Inputs + addr, e0, e1, e2 + ); + + output [7:0] data; + + input [6:0] addr; + input [6:0] e0; + input [5:0] e1, e2; + + reg [7:0] data; + + always @(/*AS*/addr or e0 or e1 or e2) + begin + case (addr) + `AI: data = {e0[6], 1'b0, e0[5:0]}; + `AI+1: data = e1; + `AI+2, + `AI+3: data = e2; + default: data = 0; + endcase + end + +endmodule + +// Local Variables: +// eval:(verilog-read-defines) +// verilog-auto-sense-defines-constant: t +// End: diff --git a/test_regress/t/t_case_x_bad.pl b/test_regress/t/t_case_x_bad.pl new file mode 100755 index 000000000..f55bc7b3d --- /dev/null +++ b/test_regress/t/t_case_x_bad.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +compile ( + fails=>1, + expect=> +'%Error: t/t_case_x_bad.v:\d+: Use of x/. constant in case statement, \(perhaps intended casex/casez\) +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_case_x_bad.v b/test_regress/t/t_case_x_bad.v new file mode 100644 index 000000000..7f0f5f4b7 --- /dev/null +++ b/test_regress/t/t_case_x_bad.v @@ -0,0 +1,21 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005-2006 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + value + ); + + input [3:0] value; + always @ (/*AS*/value) begin + case (value) + 4'b0000: $stop; + 4'b1xxx: $stop; + default: $stop; + endcase + end + +endmodule diff --git a/test_regress/t/t_clk_condflop.pl b/test_regress/t/t_clk_condflop.pl new file mode 100755 index 000000000..65c0146de --- /dev/null +++ b/test_regress/t/t_clk_condflop.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +my $fail = ($Last_Self->{v3} && verilator_version() !~ /\(ord\)/); + +compile ( + ); + +execute ( + check_finished => !$fail, + fails => $fail, + ); + +ok(1); +1; diff --git a/test_regress/t/t_clk_condflop.v b/test_regress/t/t_clk_condflop.v new file mode 100644 index 000000000..369b60d3d --- /dev/null +++ b/test_regress/t/t_clk_condflop.v @@ -0,0 +1,125 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (clk); + input clk; + + reg [0:0] d1; + reg [2:0] d3; + reg [7:0] d8; + + wire [0:0] q1; + wire [2:0] q3; + wire [7:0] q8; + + // verilator lint_off UNOPTFLAT + reg ena; + // verilator lint_on UNOPTFLAT + + condff #(12) condff + (.clk(clk), .sen(1'b0), .ena(ena), + .d({d8,d3,d1}), + .q({q8,q3,q1})); + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + //$write("%x %x %x %x\n", cyc, q8, q3, q1); + cyc <= cyc + 1; + if (cyc==1) begin + d1 <= 1'b1; d3<=3'h1; d8<=8'h11; + ena <= 1'b1; + end + if (cyc==2) begin + d1 <= 1'b0; d3<=3'h2; d8<=8'h33; + ena <= 1'b0; + end + if (cyc==3) begin + d1 <= 1'b1; d3<=3'h3; d8<=8'h44; + ena <= 1'b1; + if (q8 != 8'h11) $stop; + end + if (cyc==4) begin + d1 <= 1'b1; d3<=3'h4; d8<=8'h77; + ena <= 1'b1; + if (q8 != 8'h11) $stop; + end + if (cyc==5) begin + d1 <= 1'b1; d3<=3'h0; d8<=8'h88; + ena <= 1'b1; + if (q8 != 8'h44) $stop; + end + if (cyc==6) begin + if (q8 != 8'h77) $stop; + end + if (cyc==7) begin + if (q8 != 8'h88) $stop; + end + // + if (cyc==20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule + +module condff (clk, sen, ena, d, q); + parameter WIDTH = 1; + input clk; + + input sen; + input ena; + input [WIDTH-1:0] d; + output [WIDTH-1:0] q; + + condffimp #(.WIDTH(WIDTH)) + imp (.clk(clk), .sen(sen), .ena(ena), .d(d), .q(q)); +endmodule + +module condffimp (clk, sen, ena, d, q); + parameter WIDTH = 1; + input clk; + input sen; + input ena; + input [WIDTH-1:0] d; + output reg [WIDTH-1:0] q; + wire gatedclk; + + clockgate clockgate (.clk(clk), .sen(sen), .ena(ena), .gatedclk(gatedclk)); + + always @(posedge gatedclk) begin + if (gatedclk === 1'bX) begin + q <= {WIDTH{1'bX}}; + end + else begin + q <= d; + end + end + +endmodule + +module clockgate (clk, sen, ena, gatedclk); + input clk; + input sen; + input ena; + output gatedclk; + + reg ena_b; + wire gatedclk = clk & ena_b; + + // verilator lint_off COMBDLY + always @(clk or ena or sen) begin + if (~clk) begin + ena_b <= ena | sen; + end + else begin + if ((clk^sen)===1'bX) ena_b <= 1'bX; + end + end + // verilator lint_on COMBDLY + +endmodule diff --git a/test_regress/t/t_clk_dpulse.pl b/test_regress/t/t_clk_dpulse.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_clk_dpulse.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_clk_dpulse.v b/test_regress/t/t_clk_dpulse.v new file mode 100644 index 000000000..79ee8836a --- /dev/null +++ b/test_regress/t/t_clk_dpulse.v @@ -0,0 +1,47 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + // verilator lint_off GENCLK + + reg [7:0] cyc; initial cyc=0; + reg genclk; + // verilator lint_off MULTIDRIVEN + reg [7:0] set_both; + // verilator lint_on MULTIDRIVEN + + wire genthiscyc = ( (cyc % 2) == 1 ); + + always @ (posedge clk) begin + cyc <= cyc + 8'h1; + genclk <= genthiscyc; + set_both <= cyc; + $write ("SB set_both %x <= cyc %x\n", set_both, cyc); + if (genthiscyc) begin + if (cyc>1 && set_both != (cyc - 8'h1)) $stop; + end + else begin + if (cyc>1 && set_both != ~(cyc - 8'h1)) $stop; + end + if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always @ (posedge genclk) begin + set_both <= ~ set_both; + $write ("SB set_both %x <= cyc %x\n", set_both, ~cyc); + if (cyc>1 && set_both != (cyc - 8'h1)) $stop; + end + +endmodule diff --git a/test_regress/t/t_clk_dsp.pl b/test_regress/t/t_clk_dsp.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_clk_dsp.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_clk_dsp.v b/test_regress/t/t_clk_dsp.v new file mode 100644 index 000000000..f497139a1 --- /dev/null +++ b/test_regress/t/t_clk_dsp.v @@ -0,0 +1,180 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + // verilator lint_off GENCLK + + reg [7:0] cyc; initial cyc=0; + reg [7:0] padd; + reg dsp_ph1, dsp_ph2, dsp_reset; + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [7:0] out; // From dspchip of t_dspchip.v + // End of automatics + + t_dspchip dspchip (/*AUTOINST*/ + // Outputs + .out (out[7:0]), + // Inputs + .dsp_ph1 (dsp_ph1), + .dsp_ph2 (dsp_ph2), + .dsp_reset (dsp_reset), + .padd (padd[7:0])); + + always @ (posedge clk) begin + $write("cyc %d\n",cyc); + if (cyc == 8'd0) begin + cyc <= 8'd1; + dsp_reset <= 0; // Need a posedge + padd <= 0; + end + else if (cyc == 8'd20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + else begin + cyc <= cyc + 8'd1; + dsp_ph1 <= ((cyc&8'd3) == 8'd0); + dsp_ph2 <= ((cyc&8'd3) == 8'd2); + dsp_reset <= (cyc == 8'd1); + padd <= cyc; + //$write("[%0t] cyc %d %x->%x\n", $time, cyc, padd, out); + case (cyc) + default: $stop; + 8'd01: ; + 8'd02: ; + 8'd03: ; + 8'd04: ; + 8'd05: ; + 8'd06: ; + 8'd07: ; + 8'd08: ; + 8'd09: if (out!==8'h04) $stop; + 8'd10: if (out!==8'h04) $stop; + 8'd11: if (out!==8'h08) $stop; + 8'd12: if (out!==8'h08) $stop; + 8'd13: if (out!==8'h00) $stop; + 8'd14: if (out!==8'h00) $stop; + 8'd15: if (out!==8'h00) $stop; + 8'd16: if (out!==8'h00) $stop; + 8'd17: if (out!==8'h0c) $stop; + 8'd18: if (out!==8'h0c) $stop; + 8'd19: if (out!==8'h10) $stop; + endcase + end + end + +endmodule + +module t_dspchip (/*AUTOARG*/ + // Outputs + out, + // Inputs + dsp_ph1, dsp_ph2, dsp_reset, padd + ); + input dsp_ph1, dsp_ph2, dsp_reset; + input [7:0] padd; + output [7:0] out; + + wire dsp_ph1, dsp_ph2; + wire [7:0] out; + wire pla_ph1, pla_ph2; + wire out1_r; + wire [7:0] out2_r, padd; + wire clk_en; + + t_dspcore t_dspcore (/*AUTOINST*/ + // Outputs + .out1_r (out1_r), + .pla_ph1 (pla_ph1), + .pla_ph2 (pla_ph2), + // Inputs + .dsp_ph1 (dsp_ph1), + .dsp_ph2 (dsp_ph2), + .dsp_reset (dsp_reset), + .clk_en (clk_en)); + t_dsppla t_dsppla (/*AUTOINST*/ + // Outputs + .out2_r (out2_r[7:0]), + // Inputs + .pla_ph1 (pla_ph1), + .pla_ph2 (pla_ph2), + .dsp_reset (dsp_reset), + .padd (padd[7:0])); + + assign out = out1_r ? 8'h00 : out2_r; + assign clk_en = 1'b1; + +endmodule + +module t_dspcore (/*AUTOARG*/ + // Outputs + out1_r, pla_ph1, pla_ph2, + // Inputs + dsp_ph1, dsp_ph2, dsp_reset, clk_en + ); + input dsp_ph1, dsp_ph2, dsp_reset; + input clk_en; + output out1_r, pla_ph1, pla_ph2; + + wire dsp_ph1, dsp_ph2, dsp_reset; + wire pla_ph1, pla_ph2; + reg out1_r; + + always @(posedge dsp_ph1 or posedge dsp_reset) begin + if (dsp_reset) + out1_r <= 1'h0; + else + out1_r <= ~out1_r; + end + + assign pla_ph1 = dsp_ph1; + assign pla_ph2 = dsp_ph2 & clk_en; + +endmodule + +module t_dsppla (/*AUTOARG*/ + // Outputs + out2_r, + // Inputs + pla_ph1, pla_ph2, dsp_reset, padd + ); + input pla_ph1, pla_ph2, dsp_reset; + input [7:0] padd; + output [7:0] out2_r; + + wire pla_ph1, pla_ph2, dsp_reset; + wire [7:0] padd; + reg [7:0] out2_r; + + reg [7:0] latched_r; + + always @(posedge pla_ph1 or posedge dsp_reset) begin + if (dsp_reset) + latched_r <= 8'h00; + else + latched_r <= padd; + end + + always @(posedge pla_ph2 or posedge dsp_reset) begin + if (dsp_reset) + out2_r <= 8'h00; + else + out2_r <= latched_r; + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_clk_gen.pl b/test_regress/t/t_clk_gen.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_clk_gen.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_clk_gen.v b/test_regress/t/t_clk_gen.v new file mode 100644 index 000000000..ec9e929a5 --- /dev/null +++ b/test_regress/t/t_clk_gen.v @@ -0,0 +1,91 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + // verilator lint_off GENCLK + reg gendlyclk_r; + reg [31:0] gendlydata_r; + reg [31:0] dlydata_gr; + + reg genblkclk; + reg [31:0] genblkdata; + reg [31:0] blkdata_gr; + + wire [31:0] constwire = 32'h11; + reg [31:0] initwire; + + integer i; + initial begin + for (i=0; i<10000; i=i+1) begin + initwire = 32'h2200; + end + end + + wire [31:0] either = gendlydata_r | dlydata_gr | blkdata_gr | initwire | constwire; + wire [31:0] either_unused = gendlydata_r | dlydata_gr | blkdata_gr | initwire | constwire; + + always @ (posedge clk) begin + gendlydata_r <= 32'h0011_0000; + gendlyclk_r <= 0; + // surefire lint_off SEQASS + genblkclk = 0; + genblkdata = 0; + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==2) begin + gendlyclk_r <= 1; + gendlydata_r <= 32'h00540000; + genblkclk = 1; + genblkdata = 32'hace; + $write("[%0t] Send pulse\n", $time); + end + if (cyc==3) begin + genblkdata = 32'hdce; + gendlydata_r <= 32'h00ff0000; + if (either != 32'h87542211) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + // surefire lint_on SEQASS + end + + always @ (posedge gendlyclk_r) begin + if ($time>0) begin // Hack, don't split the block + $write("[%0t] Got gendlyclk_r, d=%x b=%x\n", $time, gendlydata_r, genblkdata); + dlydata_gr <= 32'h80000000; + // Delayed activity list will already be completed for gendlydata + // because genclk is from a delayed assignment. + // Thus we get the NEW not old value of gendlydata_r + if (gendlydata_r != 32'h00540000) $stop; + if (genblkdata != 32'hace) $stop; + end + end + + always @ (posedge genblkclk) begin + if ($time>0) begin // Hack, don't split the block + $write("[%0t] Got genblkclk, d=%x b=%x\n", $time, gendlydata_r, genblkdata); + blkdata_gr <= 32'h07000000; + // Clock from non-delayed assignment, we get old value of gendlydata_r +`ifdef verilator `else // V3.2 races... technically legal + if (gendlydata_r != 32'h00110000) $stop; +`endif + if (genblkdata != 32'hace) $stop; + end + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_clk_latch.pl b/test_regress/t/t_clk_latch.pl new file mode 100755 index 000000000..65c0146de --- /dev/null +++ b/test_regress/t/t_clk_latch.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +my $fail = ($Last_Self->{v3} && verilator_version() !~ /\(ord\)/); + +compile ( + ); + +execute ( + check_finished => !$fail, + fails => $fail, + ); + +ok(1); +1; diff --git a/test_regress/t/t_clk_latch.v b/test_regress/t/t_clk_latch.v new file mode 100644 index 000000000..8a6be6619 --- /dev/null +++ b/test_regress/t/t_clk_latch.v @@ -0,0 +1,108 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + fastclk, clk + ); + +`ifdef EDGE_DETECT_STYLE // Two 'common' forms of latching, with full combo, and with pos/negedge + `define posstyle posedge + `define negstyle negedge +`else + `define posstyle + `define negstyle +`endif + + input fastclk; + input clk; + + reg [7:0] data; + reg [7:0] data_a; + reg [7:0] data_a_a; + reg [7:0] data_a_b; + reg [7:0] data_b; + reg [7:0] data_b_a; + reg [7:0] data_b_b; + + reg [8*6-1:0] check [100:0]; + wire [8*6-1:0] compare = {data_a,data_a_a,data_b_a,data_b,data_a_b,data_b_b}; + initial begin + check[7'd19] = {8'h0d, 8'h0e, 8'h0e, 8'h0d, 8'h0e, 8'h0e}; + check[7'd20] = {8'h0d, 8'h0e, 8'h0e, 8'h0d, 8'h0e, 8'h0e}; + check[7'd21] = {8'h15, 8'h16, 8'h0e, 8'h0d, 8'h0e, 8'h0e}; + check[7'd22] = {8'h15, 8'h16, 8'h0e, 8'h0d, 8'h0e, 8'h0e}; + check[7'd23] = {8'h15, 8'h16, 8'h0e, 8'h15, 8'h16, 8'h0e}; + check[7'd24] = {8'h15, 8'h16, 8'h0e, 8'h15, 8'h16, 8'h0e}; + check[7'd25] = {8'h15, 8'h16, 8'h0e, 8'h15, 8'h16, 8'h0e}; + check[7'd26] = {8'h15, 8'h16, 8'h16, 8'h15, 8'h16, 8'h0e}; + check[7'd27] = {8'h15, 8'h16, 8'h16, 8'h15, 8'h16, 8'h0e}; + check[7'd28] = {8'h15, 8'h16, 8'h16, 8'h15, 8'h16, 8'h16}; + check[7'd29] = {8'h15, 8'h16, 8'h16, 8'h15, 8'h16, 8'h16}; + check[7'd30] = {8'h15, 8'h16, 8'h16, 8'h15, 8'h16, 8'h16}; + check[7'd31] = {8'h1f, 8'h20, 8'h16, 8'h15, 8'h16, 8'h16}; + check[7'd32] = {8'h1f, 8'h20, 8'h16, 8'h15, 8'h16, 8'h16}; + check[7'd33] = {8'h1f, 8'h20, 8'h16, 8'h1f, 8'h20, 8'h16}; + check[7'd34] = {8'h1f, 8'h20, 8'h16, 8'h1f, 8'h20, 8'h16}; + check[7'd35] = {8'h1f, 8'h20, 8'h16, 8'h1f, 8'h20, 8'h16}; + check[7'd36] = {8'h1f, 8'h20, 8'h20, 8'h1f, 8'h20, 8'h16}; + check[7'd37] = {8'h1f, 8'h20, 8'h20, 8'h1f, 8'h20, 8'h16}; + end + + // verilator lint_off COMBDLY + always @ (`posstyle clk /*AS*/ or data) begin + if (clk) begin + data_a <= data + 8'd1; + end + end + + always @ (`posstyle clk /*AS*/ or data_a) begin + if (clk) begin + data_a_a <= data_a + 8'd1; + end + end + + always @ (`posstyle clk /*AS*/ or data_b) begin + if (clk) begin + data_b_a <= data_b + 8'd1; + end + end + + always @ (`negstyle clk /*AS*/ or data or data_a) begin + if (~clk) begin + data_b <= data + 8'd1; + data_a_b <= data_a + 8'd1; + data_b_b <= data_b + 8'd1; + end + end + + integer cyc; initial cyc=0; + + always @ (posedge fastclk) begin + cyc <= cyc+1; + $write("%d %x %x %x %x %x %x\n",cyc,data_a,data_a_a,data_b_a,data_b,data_a_b,data_b_b); + if (cyc>=19 && cyc<36) begin + if (compare !== check[cyc]) begin + $write("[%0t] Mismatch, got=%x, exp=%x\n", $time, compare, check[cyc]); + $stop; + end + end + if (cyc == 10) begin + data <= 8'd12; + end + if (cyc == 20) begin + data <= 8'd20; + end + if (cyc == 30) begin + data <= 8'd30; + end + if (cyc == 40) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_clk_latch_edgestyle.pl b/test_regress/t/t_clk_latch_edgestyle.pl new file mode 100755 index 000000000..20e309b21 --- /dev/null +++ b/test_regress/t/t_clk_latch_edgestyle.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +top_filename("t/t_clk_latch.v"); + +my $fail = ($Last_Self->{v3} && verilator_version() !~ /\(ord\)/); + +compile ( + v_flags2 => ['+define+EDGE_DETECT_STYLE'], + fails => $fail, + ); + +execute ( + check_finished => !$fail, + ) if !$fail; + +ok(1); +1; diff --git a/test_regress/t/t_clk_powerdn.pl b/test_regress/t/t_clk_powerdn.pl new file mode 100755 index 000000000..65c0146de --- /dev/null +++ b/test_regress/t/t_clk_powerdn.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +my $fail = ($Last_Self->{v3} && verilator_version() !~ /\(ord\)/); + +compile ( + ); + +execute ( + check_finished => !$fail, + fails => $fail, + ); + +ok(1); +1; diff --git a/test_regress/t/t_clk_powerdn.v b/test_regress/t/t_clk_powerdn.v new file mode 100644 index 000000000..f15992ef0 --- /dev/null +++ b/test_regress/t/t_clk_powerdn.v @@ -0,0 +1,121 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg reset_l; + + // verilator lint_off GENCLK + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + // End of automatics + + reg clkgate_e2r; + reg clkgate_e1r_l; + always @(posedge clk or negedge reset_l) begin + if (!reset_l) begin + clkgate_e1r_l <= ~1'b1; + end + else begin + clkgate_e1r_l <= ~clkgate_e2r; + end + end + + reg clkgate_e1f; + always @(negedge clk) begin + // Yes, it's really a = + clkgate_e1f = ~clkgate_e1r_l | ~reset_l; + end + + wire clkgated = clk & clkgate_e1f; + + reg [31:0] countgated; + always @(posedge clkgated or negedge reset_l) begin + if (!reset_l) begin + countgated <= 32'h1000; + end + else begin + countgated <= countgated + 32'd1; + end + end + + reg [31:0] count; + always @(posedge clk or negedge reset_l) begin + if (!reset_l) begin + count <= 32'h1000; + end + else begin + count <= count + 32'd1; + end + end + + reg [7:0] cyc; initial cyc=0; + always @ (posedge clk) begin + $write("[%0t] rs %x cyc %d cg1f %x cnt %x cg %x\n",$time,reset_l,cyc,clkgate_e1f,count,countgated); + cyc <= cyc + 8'd1; + case (cyc) + 8'd00: begin + reset_l <= ~1'b0; + clkgate_e2r <= 1'b1; + end + 8'd01: begin + reset_l <= ~1'b0; + end + 8'd02: begin + end + 8'd03: begin + reset_l <= ~1'b1; // Need a posedge + end + 8'd04: begin + end + 8'd05: begin + reset_l <= ~1'b0; + end + 8'd09: begin + clkgate_e2r <= 1'b0; + end + 8'd11: begin + clkgate_e2r <= 1'b1; + end + 8'd20: begin + $write("*-* All Finished *-*\n"); + $finish; + end + default: ; + endcase + case (cyc) + 8'd00: ; + 8'd01: ; + 8'd02: ; + 8'd03: ; + 8'd04: if (count!=32'h00001000 || countgated!=32'h 00001000) $stop; + 8'd05: if (count!=32'h00001000 || countgated!=32'h 00001000) $stop; + 8'd06: if (count!=32'h00001000 || countgated!=32'h 00001000) $stop; + 8'd07: if (count!=32'h00001001 || countgated!=32'h 00001001) $stop; + 8'd08: if (count!=32'h00001002 || countgated!=32'h 00001002) $stop; + 8'd09: if (count!=32'h00001003 || countgated!=32'h 00001003) $stop; + 8'd10: if (count!=32'h00001004 || countgated!=32'h 00001004) $stop; + 8'd11: if (count!=32'h00001005 || countgated!=32'h 00001005) $stop; + 8'd12: if (count!=32'h00001006 || countgated!=32'h 00001005) $stop; + 8'd13: if (count!=32'h00001007 || countgated!=32'h 00001005) $stop; + 8'd14: if (count!=32'h00001008 || countgated!=32'h 00001006) $stop; + 8'd15: if (count!=32'h00001009 || countgated!=32'h 00001007) $stop; + 8'd16: if (count!=32'h0000100a || countgated!=32'h 00001008) $stop; + 8'd17: if (count!=32'h0000100b || countgated!=32'h 00001009) $stop; + 8'd18: if (count!=32'h0000100c || countgated!=32'h 0000100a) $stop; + 8'd19: if (count!=32'h0000100d || countgated!=32'h 0000100b) $stop; + 8'd20: if (count!=32'h0000100e || countgated!=32'h 0000100c) $stop; + default: $stop; + endcase + end + +endmodule diff --git a/test_regress/t/t_delay.pl b/test_regress/t/t_delay.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_delay.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_delay.v b/test_regress/t/t_delay.v new file mode 100644 index 000000000..98f681a13 --- /dev/null +++ b/test_regress/t/t_delay.v @@ -0,0 +1,37 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + parameter PAR = 3; + + input clk; + integer cyc=1; + + reg [31:0] dly0; + wire [31:0] dly1; + wire [31:0] dly2 = dly1 + 32'h1; + + assign #(1.2000000000000000) dly1 = dly0 + 32'h1; + + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc==1) begin + dly0 <= #0 32'h11; + end + else if (cyc==2) begin + dly0 <= #0.12 dly0 + 32'h12; + end + else if (cyc==3) begin + if (dly0 !== 32'h23) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_display.pl b/test_regress/t/t_display.pl new file mode 100755 index 000000000..34012d920 --- /dev/null +++ b/test_regress/t/t_display.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + expect=> +'\[0\] (%m|.*v): Hi'.quotemeta(' +[0] %X=0c %D=12 %0X=c %0O=14 %B=001100 +[0] %x=0c %d=12 %0x=c %0o=14 %b=001100 +[0] %x=00abbbbcccc %0x=abbbbcccc %o=00527356746314 %b=00000101010111011101110111100110011001100 +[0] %x=00abc1234567812345678 %0x=abc1234567812345678 %o=012570110642547402215053170 %b=000001010101111000001001000110100010101100111100000010010001101000101011001111000 + +[0] %s=! %s= what! %s= hmmm!1234 +'), + ); + +ok(1); +1; diff --git a/test_regress/t/t_display.v b/test_regress/t/t_display.v new file mode 100644 index 000000000..b582565e8 --- /dev/null +++ b/test_regress/t/t_display.v @@ -0,0 +1,39 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t; + reg [40:0] quad; initial quad = 41'ha_bbbb_cccc; + reg [80:0] wide; initial wide = 81'habc_1234_5678_1234_5678; + reg [31:0] str; initial str = "\000\277\021\n"; + reg [47:0] str2; initial str2 = "\000what!"; + reg [79:0] str3; initial str3 = "\000hmmm!1234"; + initial begin + $write("[%0t] ", $time); + $write("%m: Hi\n"); + + // Display formatting + $display("[%0t] %%X=%X %%D=%D %%0X=%0X %%0O=%0O %%B=%B", $time, + quad[5:0], quad[5:0], quad[5:0], quad[5:0], quad[5:0]); + $display("[%0t] %%x=%x %%d=%d %%0x=%0x %%0o=%0o %%b=%b", $time, + quad[5:0], quad[5:0], quad[5:0], quad[5:0], quad[5:0]); + $display("[%0t] %%x=%x %%0x=%0x %%o=%o %%b=%b", $time, + quad, quad, quad, quad); + $display("[%0t] %%x=%x %%0x=%0x %%o=%o %%b=%b", $time, + wide, wide, wide, wide); + $display; + // Not testing %0s, it does different things in different simulators + $display("[%0t] %%s=%s %%s=%s %%s=%s", $time, + str2[7:0], str2, str3); + + // Str check +`ifndef nc // NC-Verilog 5.3 chokes on this test + if (str !== 32'h00_bf_11_0a) $stop; +`endif + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_display_bad.pl b/test_regress/t/t_display_bad.pl new file mode 100755 index 000000000..e6c7b5549 --- /dev/null +++ b/test_regress/t/t_display_bad.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>1, + expect=> +'%Error: t/t_display_bad.v:\d+: Missing arguments for \$display format +%Error: t/t_display_bad.v:\d+: Extra arguments for \$display format +%Error: t/t_display_bad.v:\d+: Unknown \$display format code: %q +%Error: Exiting due to.*', + ); + +ok(1); +1; + diff --git a/test_regress/t/t_display_bad.v b/test_regress/t/t_display_bad.v new file mode 100644 index 000000000..130a9c0e4 --- /dev/null +++ b/test_regress/t/t_display_bad.v @@ -0,0 +1,17 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t; + reg [40:0] disp; initial disp = 41'ha_bbbb_cccc; + initial begin + // Display formatting + $display("%x"); // Too few + $display("%x",disp,disp); // Too many + $display("%q"); // Bad escape + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_display_signed.pl b/test_regress/t/t_display_signed.pl new file mode 100755 index 000000000..ca163b6c9 --- /dev/null +++ b/test_regress/t/t_display_signed.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + expect=> quotemeta( +'[0] %x=0bbccc %x=0bbccc %o=2736314 %b=010111011110011001100 %0d=769228 %d= 769228 +[0] %x=1bbccc %x=1bbccc %o=6736314 %b=110111011110011001100 %0d=-279348 %d= -279348 +[0] %x=000bbbbcccc %x=000bbbbcccc %o=00027356746314 %b=00000000010111011101110111100110011001100 %0d=3149647052 %d= 3149647052 +[0] %x=100bbbbcccc %x=100bbbbcccc %o=20027356746314 %b=10000000010111011101110111100110011001100 %0d=-1096361980724 %d=-1096361980724 +[0] %x=000bc1234567812345678 %x=000bc1234567812345678 %o=000570110642547402215053170 %b=000000000101111000001001000110100010101100111100000010010001101000101011001111000 +[0] %x=000bc1234577812345678 %x=000bc1234577812345678 %o=000570110642567402215053170 %b=000000000101111000001001000110100010101110111100000010010001101000101011001111000 +'), + ); + +ok(1); +1; diff --git a/test_regress/t/t_display_signed.v b/test_regress/t/t_display_signed.v new file mode 100644 index 000000000..774da92e8 --- /dev/null +++ b/test_regress/t/t_display_signed.v @@ -0,0 +1,33 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t; + reg signed [20:0] longp; initial longp = 21'shbbccc; + reg signed [20:0] longn; initial longn = 21'shbbccc; initial longn[20]=1'b1; + reg signed [40:0] quadp; initial quadp = 41'shbbbb_cccc; + reg signed [40:0] quadn; initial quadn = 41'shbbbb_cccc; initial quadn[40]=1'b1; + reg signed [80:0] widep; initial widep = 81'shbc_1234_5678_1234_5678; + reg signed [80:0] widen; initial widen = 81'shbc_1234_5678_1234_5678; initial widen[40]=1'b1; + + initial begin + // Display formatting + $display("[%0t] %%x=%x %%x=%x %%o=%o %%b=%b %%0d=%0d %%d=%d", $time, + longp, longp, longp, longp, longp, longp); + $display("[%0t] %%x=%x %%x=%x %%o=%o %%b=%b %%0d=%0d %%d=%d", $time, + longn, longn, longn, longn, longn, longn); + $display("[%0t] %%x=%x %%x=%x %%o=%o %%b=%b %%0d=%0d %%d=%d", $time, + quadp, quadp, quadp, quadp, quadp, quadp); + $display("[%0t] %%x=%x %%x=%x %%o=%o %%b=%b %%0d=%0d %%d=%d", $time, + quadn, quadn, quadn, quadn, quadn, quadn); + $display("[%0t] %%x=%x %%x=%x %%o=%o %%b=%b", $time, + widep, widep, widep, widep); + $display("[%0t] %%x=%x %%x=%x %%o=%o %%b=%b", $time, + widen, widen, widen, widen); + $display; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_dos.pl b/test_regress/t/t_dos.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_dos.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_dos.v b/test_regress/t/t_dos.v new file mode 100755 index 000000000..24709f8b4 --- /dev/null +++ b/test_regress/t/t_dos.v @@ -0,0 +1,21 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +// This file has DOS carrage returns in it! +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + always @ (posedge clk) begin + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule +// This file has DOS carrage returns in it! diff --git a/test_regress/t/t_file.pl b/test_regress/t/t_file.pl new file mode 100755 index 000000000..e0ac01b53 --- /dev/null +++ b/test_regress/t/t_file.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +unlink("obj_dir/t_file_test.log"); + +execute ( + check_finished=>1, + ); + +file_grep ("obj_dir/t_file_test.log", +qr/\[0\] hello v=12345667 +\[0\] Hello2 +/); + +ok(1); +1; diff --git a/test_regress/t/t_file.v b/test_regress/t/t_file.v new file mode 100644 index 000000000..9091d02c5 --- /dev/null +++ b/test_regress/t/t_file.v @@ -0,0 +1,33 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +`include "verilated.v" + +module t; + `verilator_file_descriptor file; + + initial begin + // Display formatting +`ifdef verilator + if (file != 0) $stop; + $fwrite(file, "Never printed, file closed\n"); +`endif + + file = $fopen("obj_dir/t_file_test.log","w"); // The "w" is required so we get a FD not a MFD + + $fdisplay(file, "[%0t] hello v=%x", $time, 32'h12345667); + $fwrite(file, "[%0t] %s\n", $time, "Hello2"); + + $fclose(file); +`ifdef verilator + if (file != 0) $stop; + $fwrite(file, "Never printed, file closed\n"); +`endif + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_flag_lib.pl b/test_regress/t/t_flag_lib.pl new file mode 100644 index 000000000..d6d7d01c5 --- /dev/null +++ b/test_regress/t/t_flag_lib.pl @@ -0,0 +1,13 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } + +compile ( + v_flags2 => ['-v', 't/t_flag_libinc.v'], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_flag_lib.v b/test_regress/t/t_flag_lib.v new file mode 100644 index 000000000..0acb6d16e --- /dev/null +++ b/test_regress/t/t_flag_lib.v @@ -0,0 +1,11 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/); + + liblib_a a (); + +endmodule diff --git a/test_regress/t/t_flag_libinc.v b/test_regress/t/t_flag_libinc.v new file mode 100644 index 000000000..bf69899ec --- /dev/null +++ b/test_regress/t/t_flag_libinc.v @@ -0,0 +1,27 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module liblib_a (/*AUTOARG*/); + liblib_b b (); +endmodule + +module liblib_b (/*AUTOARG*/); + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +module liblib_c (/*AUTOARG*/); + // Unused + initial $stop; + liblib_d d (); +endmodule + +module liblib_d (/*AUTOARG*/); + // Unused + initial $stop; +endmodule diff --git a/test_regress/t/t_flag_werror.v b/test_regress/t/t_flag_werror.v new file mode 100644 index 000000000..fc5b7fc03 --- /dev/null +++ b/test_regress/t/t_flag_werror.v @@ -0,0 +1,12 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/); + + // Width error below + wire [3:0] foo = 6'h2e; + +endmodule diff --git a/test_regress/t/t_flag_werror_bad1.pl b/test_regress/t/t_flag_werror_bad1.pl new file mode 100755 index 000000000..85c0c3fa4 --- /dev/null +++ b/test_regress/t/t_flag_werror_bad1.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +top_filename("t/t_flag_werror.v"); + +compile ( + fails=>$Last_Self->{v3}, + expect=> +'%Warning-WIDTH: t/t_flag_werror.v:\d+: Operator ASSIGNW expects 4 bits on the Assign RHS, but Assign RHS.s CONST generates 6 bits. +%Warning-WIDTH: Use .* and lint_on around source to disable this message. +%Error: Exiting due to', + ) if $Last_Self->{v3}; + +ok(1); +1; diff --git a/test_regress/t/t_flag_werror_bad2.pl b/test_regress/t/t_flag_werror_bad2.pl new file mode 100755 index 000000000..f30202b04 --- /dev/null +++ b/test_regress/t/t_flag_werror_bad2.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +top_filename("t/t_flag_werror.v"); + +compile ( + fails=>$Last_Self->{v3}, + verilator_flags=> [qw(-sp -Werror-WIDTH)], + expect=> +'%Error: t/t_flag_werror.v:\d+: Operator ASSIGNW expects 4 bits on the Assign RHS, but Assign RHS.s CONST generates 6 bits. +%Error: Exiting due to', + ) if $Last_Self->{v3}; + +ok(1); +1; diff --git a/test_regress/t/t_for_count.pl b/test_regress/t/t_for_count.pl new file mode 100755 index 000000000..eff491d7a --- /dev/null +++ b/test_regress/t/t_for_count.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_for_count.v b/test_regress/t/t_for_count.v new file mode 100644 index 000000000..be0f527d3 --- /dev/null +++ b/test_regress/t/t_for_count.v @@ -0,0 +1,63 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + integer j; + integer hit_count; + reg [63:0] cam_lookup_hit_vector; + + always @(/*AUTOSENSE*/cam_lookup_hit_vector) begin + hit_count = 0; + for (j=0; j < 64; j=j+1) begin + hit_count = hit_count + {31'h0, cam_lookup_hit_vector[j]}; + end + end + + reg [127:0] wide_for_index; + reg [31:0] wide_for_count; + always @(/*AUTOSENSE*/cam_lookup_hit_vector) begin + wide_for_count = 0; + for (wide_for_index = 128'hff_00000000_00000000; + wide_for_index < 128'hff_00000000_00000100; + wide_for_index = wide_for_index + 2) begin + wide_for_count = wide_for_count+32'h1; + end + end + + always @ (posedge clk) begin + cam_lookup_hit_vector <= 0; + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + cam_lookup_hit_vector <= 64'h00010000_00010000; + end + if (cyc==2) begin + if (hit_count != 32'd2) $stop; + cam_lookup_hit_vector <= 64'h01010010_00010001; + end + if (cyc==3) begin + if (hit_count != 32'd5) $stop; + if (wide_for_count != 32'h80) $stop; + end + if (cyc==4) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_func.pl b/test_regress/t/t_func.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_func.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func.v b/test_regress/t/t_func.v new file mode 100644 index 000000000..9037ec5ae --- /dev/null +++ b/test_regress/t/t_func.v @@ -0,0 +1,73 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t; + reg [2:0] value; + reg [31:0] global; + + initial begin + global = 1; + value = 2; + if (add(value) != 3'd3) $stop; + if (global != 2) $stop; + if (add(add(3'd1)) != 3'd3) $stop; + if (global != 4) $stop; + if (munge4(4'b0010) != 4'b1011) $stop; + if (global != 5) $stop; + setit; + incr(global,global,32'h10); + if (global != 32'h17) $stop; + nop(32'h11); + + $write("*-* All Finished *-*\n"); + $finish; + end + + function [2:0] add; + input [2:0] from; + begin + add = from + 3'd1; + begin : named + reg [31:0] flocal; + flocal = 1; + global = global + flocal; + end + end + endfunction + + function [3:0] munge4; + input [3:0] from; // Different from the 'from' signal above + reg one; + begin : named + reg [1:0] flocal; + // Function calling a function + one = 1'b1; + munge4 = {one, add(from[2:0])}; + end + endfunction + + task setit; + reg [31:0] temp; + begin + temp = global + 32'h1; + global = temp + 32'h1; + end + endtask + + task incr; + output [31:0] z; + input [31:0] a; + input [31:0] inc; + z = a + inc; + endtask + + task nop; + input [31:0] a; + begin + end + endtask + +endmodule diff --git a/test_regress/t/t_func_bad.pl b/test_regress/t/t_func_bad.pl new file mode 100755 index 000000000..b75ab14ad --- /dev/null +++ b/test_regress/t/t_func_bad.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>1, + expect=> +'%Error: t/t_func_bad.v:\d+: Too few arguments in function call +%Error: t/t_func_bad.v:\d+: Too many arguments in function call +%Error: t/t_func_bad.v:\d+: Too few arguments in function call +%Error: t/t_func_bad.v:\d+: Unsupported: Task output pin connected to non-variable +%Error: t/t_func_bad.v:\d+: Outputs not allowed in function declarations +%Error: Exiting due to', + ); + +ok(1); +1; + diff --git a/test_regress/t/t_func_bad.v b/test_regress/t/t_func_bad.v new file mode 100644 index 000000000..b1d8cd206 --- /dev/null +++ b/test_regress/t/t_func_bad.v @@ -0,0 +1,33 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t; + initial begin + if (add(3'd1) != 0) $stop; // Too few args + if (add(3'd1, 3'd2, 3'd3) != 0) $stop; // Too many args + x; // Too few args + if (hasout(3'd1) != 0) $stop; // outputs + end + + function [2:0] add; + input [2:0] from1; + input [2:0] from2; + begin + add = from1 + from2; + end + endfunction + + task x; + output y; + begin end + endtask + + function hasout; + output [2:0] illegal_output; + hasout = 0; + endfunction + +endmodule diff --git a/test_regress/t/t_func_bad2.pl b/test_regress/t/t_func_bad2.pl new file mode 100755 index 000000000..98b735806 --- /dev/null +++ b/test_regress/t/t_func_bad2.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>$Last_Self->{v3}, + expect=> +'%Error: t/t_func_bad2.v:\d+: Unsupported: Recursive function or task call +%Error: Exiting due to', + ); + +ok(1); +1; + diff --git a/test_regress/t/t_func_bad2.v b/test_regress/t/t_func_bad2.v new file mode 100644 index 000000000..1cf093a73 --- /dev/null +++ b/test_regress/t/t_func_bad2.v @@ -0,0 +1,18 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t; + function recurse; + input i; + recurse = recurse2(i); + endfunction + + function recurse2; + input i; + recurse2 = recurse(i); + endfunction + +endmodule diff --git a/test_regress/t/t_func_bad_width.pl b/test_regress/t/t_func_bad_width.pl new file mode 100755 index 000000000..2e950b630 --- /dev/null +++ b/test_regress/t/t_func_bad_width.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>$Last_Self->{v3}, + expect=> +'%Warning-WIDTH: t/t_func_bad_width.v:\d+: Operator FUNCREF expects 40 bits on the Function Argument, but Function Argument.s VARREF generates 39 bits. +%Warning-WIDTH: Use [^\n]+ +%Warning-WIDTH: t/t_func_bad_width.v:\d+: Operator ASSIGN expects 4 bits on the Assign RHS, but Assign RHS.s FUNCREF generates 32 bits. +%Error: Exiting due to', + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_bad_width.v b/test_regress/t/t_func_bad_width.v new file mode 100644 index 000000000..f9fa5e08c --- /dev/null +++ b/test_regress/t/t_func_bad_width.v @@ -0,0 +1,23 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t; + + reg [3:0] out; + reg [38:0] in; + initial begin + in = 39'h0; + out = MUX (in); + $write("bad widths %x", out); + end + + function [31:0] MUX; + input [39:0] XX ; + begin + MUX = XX[39:8]; + end + endfunction +endmodule diff --git a/test_regress/t/t_func_crc.pl b/test_regress/t/t_func_crc.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_func_crc.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_crc.v b/test_regress/t/t_func_crc.v new file mode 100644 index 000000000..ccacc639a --- /dev/null +++ b/test_regress/t/t_func_crc.v @@ -0,0 +1,144 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [63:0] d; + reg [31:0] c; + + wire [31:0] q = crc (d, c); + reg [31:0] q_r; + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + q_r <= q; + c <= q; + d <= {d[62:0], ^d[63:48]}; + //$write("%d crc(%x,%x)=%x\n", cyc, d, c, q); + if (cyc==1) begin + // Assign inputs randomly + q_r <= 32'h12345678; + c <= 32'h12345678; + d <= 64'hffffffff_ffffffff; + end + if (cyc==2) begin + d <= 64'hffffffff_ffffffff; + end + if (cyc==3) begin + d <= 64'hffffffff_ffffffff; + end + if (cyc==4) begin + d <= 64'h50183721_81a04b1a; + end + if (cyc==5) begin + end + if (cyc==9) begin + if (q !== 32'h38295e96) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + + function [31:0] crc; + input [63:0] di; + input [31:0] ci; + reg [63:0] drev; + begin + drev = reverse(di); + crc = newcrc(drev, ci); + end + endfunction + + function [63:0] reverse; + input [63:0] di; + integer i; + begin + reverse = 64'b0; + for (i=0; i<64; i=i+1) reverse[i] = di[63-i]; + end + endfunction + + function [31:0] newcrc; + input [63:0] D; + input [31:0] C; + reg [31:0] N; + reg [31:0] DT; + begin + N = 32'b0; + // Note this isn't a real CRC code; it's been munged for privacy + N[0] = D[59]^D[53]^D[52]^D[49]^D[44]^D[41]^D[40]^D[39]^D[37]^D[32]^D[29]^D[26]^D[22]^D[21]^D[20]^D[16]^D[15]^D[14]^D[9]^D[7]^D[0] + ^C[29]^C[27]^C[24]^C[23]^C[22]^C[21]^C[19]^C[15]^C[13]^C[10]^C[8]^C[3]^C[1]; + N[1] = D[61]^D[57]^D[51]^D[47]^D[43]^D[37]^D[35]^D[32]^D[28]^D[24]^D[22]^D[21]^D[20]^D[16]^D[12]^D[11]^D[10]^D[8]^D[7]^D[6]^D[1]^D[0] + ^C[30]^C[27]^C[26]^C[20]^C[16]^C[14]^C[13]^C[11]^C[10]^C[8]^C[5]^C[0]; + N[2] = D[63]^D[62]^D[61]^D[60]^D[55]^D[54]^D[52]^D[44]^D[43]^D[42]^D[37]^D[34]^D[33]^D[29]^D[28]^D[25]^D[24]^D[23]^D[22]^D[18]^D[16]^D[15]^D[13]^D[12]^D[11] + ^C[31]^C[30]^C[27]^C[22]^C[21]^C[18]^C[15]^C[12]^C[11]^C[10]^C[7]; + N[3] = D[62]^D[54]^D[50]^D[47]^D[46]^D[38]^D[36]^D[35]^D[34]^D[33]^D[32]^D[30]^D[27]^D[25]^D[21]^D[20]^D[19]^D[17]^D[15]^D[11]^D[8]^D[5]^D[3]^D[1]^D[0] + ^C[28]^C[25]^C[24]^C[13]^C[11]^C[9]^C[8]^C[7]^C[3]^C[1]; + N[4] = D[57]^D[54]^D[53]^D[52]^D[45]^D[44]^D[43]^D[39]^D[37]^D[34]^D[33]^D[32]^D[31]^D[28]^D[24]^D[23]^D[20]^D[19]^D[15]^D[14]^D[10]^D[6]^D[1]^D[0] + ^C[30]^C[24]^C[20]^C[16]^C[14]^C[11]^C[8]^C[7]^C[6]^C[5]^C[2]; + N[5] = D[58]^D[57]^D[50]^D[49]^D[48]^D[47]^D[43]^D[39]^D[29]^D[26]^D[23]^D[22]^D[20]^D[18]^D[14]^D[10]^D[9]^D[6]^D[5]^D[4]^D[1] + ^C[27]^C[24]^C[20]^C[19]^C[18]^C[14]^C[13]^C[12]^C[11]^C[8]^C[7]^C[1]; + N[6] = D[63]^D[62]^D[61]^D[57]^D[51]^D[50]^D[47]^D[38]^D[37]^D[34]^D[30]^D[28]^D[27]^D[25]^D[21]^D[16]^D[15]^D[10]^D[9]^D[6]^D[5]^D[2]^D[1] + ^C[31]^C[27]^C[25]^C[16]^C[13]^C[9]^C[8]^C[7]^C[0]; + N[7] = ^D[62]^D[61]^D[59]^D[54]^D[52]^D[51]^D[49]^D[46]^D[45]^D[42]^D[41]^D[38]^D[35]^D[29]^D[26]^D[24]^D[15]^D[12]^D[11]^D[9]^D[2]^D[0] + ^C[28]^C[27]^C[26]^C[20]^C[19]^C[18]^C[15]^C[12]^C[7]^C[4]; + N[8] = D[62]^D[61]^D[60]^D[59]^D[52]^D[50]^D[48]^D[47]^D[46]^D[45]^D[44]^D[42]^D[41]^D[40]^D[30]^D[24]^D[23]^D[22]^D[19]^D[17]^D[11]^D[10]^D[7]^D[6]^D[2] + ^C[31]^C[29]^C[27]^C[22]^C[21]^C[19]^C[17]^C[11]^C[9]^C[7]^C[6]; + N[9] = D[62]^D[59]^D[58]^D[57]^D[54]^D[51]^D[50]^D[43]^D[41]^D[39]^D[28]^D[25]^D[24]^D[23]^D[22]^D[21]^D[18]^D[16]^D[15]^D[7] + ^C[30]^C[29]^C[27]^C[25]^C[23]^C[22]^C[13]^C[12]^C[7]^C[6]^C[5]^C[1]; + N[10] = D[61]^D[60]^D[58]^D[56]^D[54]^D[53]^D[51]^D[48]^D[46]^D[43]^D[42]^D[38]^D[37]^D[35]^D[33]^D[31]^D[30]^D[27]^D[26]^D[24]^D[19]^D[10]^D[8]^D[6]^D[1] + ^C[31]^C[30]^C[26]^C[25]^C[24]^C[21]^C[16]^C[12]^C[3]^C[2]; + N[11] = D[59]^D[57]^D[56]^D[50]^D[49]^D[48]^D[47]^D[46]^D[45]^D[42]^D[41]^D[40]^D[33]^D[32]^D[30]^D[25]^D[21]^D[15]^D[14]^D[13]^D[12]^D[11]^D[5]^D[1] + ^C[27]^C[25]^C[24]^C[21]^C[16]^C[12]^C[7]^C[3]^C[2]^C[1]; + N[12] = D[62]^D[61]^D[59]^D[58]^D[56]^D[55]^D[53]^D[48]^D[47]^D[44]^D[43]^D[35]^D[31]^D[30]^D[28]^D[24]^D[23]^D[21]^D[14]^D[5]^D[2] + ^C[28]^C[26]^C[25]^C[23]^C[22]^C[18]^C[16]^C[15]^C[6]; + N[13] = D[63]^D[60]^D[58]^D[57]^D[55]^D[54]^D[53]^D[51]^D[47]^D[45]^D[42]^D[41]^D[38]^D[28]^D[26]^D[25]^D[22]^D[20]^D[18]^D[17]^D[15]^D[13]^D[12]^D[11] + ^C[29]^C[28]^C[25]^C[22]^C[19]^C[17]^C[16]^C[15]^C[14]^C[12]^C[10]^C[9]; + N[14] = D[58]^D[56]^D[55]^D[52]^D[47]^D[43]^D[41]^D[40]^D[39]^D[38]^D[30]^D[26]^D[25]^D[22]^D[19]^D[17]^D[13]^D[11]^D[10]^D[9]^D[8]^D[3]^D[2]^D[0] + ^C[31]^C[28]^C[20]^C[18]^C[17]^C[16]^C[15]^C[13]^C[11]^C[4]^C[2]^C[1]; + N[15] = D[63]^D[62]^D[61]^D[59]^D[58]^D[48]^D[47]^D[43]^D[42]^D[35]^D[28]^D[26]^D[25]^D[24]^D[23]^D[22]^D[21]^D[20]^D[19]^D[17]^D[11]^D[7]^D[2] + ^C[30]^C[29]^C[27]^C[24]^C[20]^C[17]^C[16]^C[15]^C[11]^C[9]^C[5]; + N[16] = D[60]^D[57]^D[49]^D[46]^D[45]^D[43]^D[39]^D[36]^D[32]^D[30]^D[29]^D[28]^D[27]^D[26]^D[23]^D[20]^D[19]^D[17]^D[11]^D[8]^D[5]^D[1] + ^C[28]^C[26]^C[23]^C[22]^C[18]^C[16]^C[13]^C[12]^C[10]^C[9]^C[6]; + N[17] = D[63]^D[62]^D[61]^D[60]^D[58]^D[54]^D[53]^D[51]^D[48]^D[42]^D[41]^D[37]^D[36]^D[34]^D[28]^D[27]^D[26]^D[24]^D[13]^D[12]^D[9]^D[7]^D[4]^D[0] + ^C[31]^C[30]^C[27]^C[23]^C[20]^C[17]^C[14]^C[9]^C[6]^C[4]^C[3]^C[0]; + N[18] = D[63]^D[61]^D[59]^D[56]^D[52]^D[50]^D[47]^D[42]^D[37]^D[35]^D[34]^D[31]^D[30]^D[29]^D[22]^D[19]^D[17]^D[16]^D[11]^D[9]^D[8]^D[7] + ^C[26]^C[22]^C[20]^C[19]^C[16]^C[11]^C[8]^C[6]^C[5]^C[0]; + N[19] = D[62]^D[60]^D[52]^D[49]^D[44]^D[43]^D[42]^D[37]^D[33]^D[32]^D[29]^D[26]^D[19]^D[17]^D[16]^D[12]^D[10]^D[7]^D[6]^D[4]^D[3]^D[2] + ^C[30]^C[29]^C[26]^C[25]^C[22]^C[19]^C[14]^C[7]^C[6]^C[5]^C[2]^C[0]; + N[20] = D[63]^D[58]^D[54]^D[48]^D[47]^D[40]^D[39]^D[35]^D[34]^D[32]^D[31]^D[28]^D[27]^D[25]^D[18]^D[12]^D[9]^D[7]^D[5]^D[4]^D[3]^D[2]^D[1] + ^C[31]^C[29]^C[28]^C[25]^C[19]^C[18]^C[17]^C[15]^C[10]^C[9]^C[6]^C[4]; + N[21] = D[61]^D[59]^D[57]^D[56]^D[53]^D[48]^D[44]^D[43]^D[41]^D[35]^D[29]^D[26]^D[25]^D[20]^D[18]^D[17]^D[16]^D[12]^D[9]^D[6]^D[5]^D[3]^D[1] + ^C[30]^C[27]^C[24]^C[23]^C[22]^C[21]^C[20]^C[13]^C[9]^C[3]^C[2]; + N[22] = D[63]^D[62]^D[60]^D[57]^D[53]^D[51]^D[45]^D[44]^D[42]^D[34]^D[33]^D[27]^D[20]^D[19]^D[18]^D[15]^D[10]^D[9]^D[8]^D[4]^D[3] + ^C[24]^C[23]^C[18]^C[17]^C[16]^C[14]^C[12]^C[11]^C[10]^C[9]^C[6]^C[5]; + N[23] = D[58]^D[56]^D[54]^D[51]^D[47]^D[43]^D[42]^D[40]^D[37]^D[36]^D[33]^D[25]^D[23]^D[20]^D[18]^D[16]^D[15]^D[12]^D[10]^D[8]^D[7]^D[5]^D[3] + ^C[31]^C[27]^C[26]^C[23]^C[21]^C[18]^C[15]^C[11]^C[10]^C[8]^C[7]^C[1]; + N[24] = D[60]^D[59]^D[52]^D[50]^D[48]^D[44]^D[39]^D[36]^D[35]^D[31]^D[30]^D[28]^D[27]^D[23]^D[22]^D[21]^D[19]^D[14]^D[13]^D[12]^D[9]^D[4]^D[1]^D[0] + ^C[27]^C[25]^C[23]^C[21]^C[17]^C[11]^C[10]^C[4]^C[0]; + N[25] = D[61]^D[60]^D[56]^D[54]^D[51]^D[46]^D[43]^D[41]^D[40]^D[38]^D[37]^D[36]^D[29]^D[28]^D[27]^D[22]^D[17]^D[15]^D[10]^D[7]^D[4]^D[2] + ^C[29]^C[28]^C[26]^C[23]^C[18]^C[14]^C[13]^C[12]^C[11]^C[9]^C[8]^C[6]; + N[26] = D[63]^D[62]^D[58]^D[55]^D[54]^D[52]^D[50]^D[39]^D[37]^D[36]^D[35]^D[33]^D[31]^D[29]^D[27]^D[18]^D[14]^D[10]^D[3]^D[2]^D[0] + ^C[31]^C[27]^C[26]^C[25]^C[24]^C[21]^C[13]^C[12]^C[10]^C[1]; + N[27] = D[62]^D[60]^D[58]^D[56]^D[55]^D[54]^D[51]^D[44]^D[41]^D[36]^D[34]^D[32]^D[31]^D[29]^D[28]^D[27]^D[23]^D[17]^D[12]^D[11]^D[8]^D[6]^D[4]^D[2] + ^C[31]^C[30]^C[28]^C[27]^C[23]^C[19]^C[17]^C[16]^C[14]^C[12]^C[11]^C[10]^C[3]; + N[28] = D[57]^D[54]^D[53]^D[51]^D[50]^D[48]^D[40]^D[38]^D[34]^D[33]^D[31]^D[30]^D[29]^D[27]^D[23]^D[21]^D[14]^D[9]^D[7]^D[6]^D[5]^D[4]^D[0] + ^C[31]^C[30]^C[26]^C[24]^C[15]^C[14]^C[13]^C[7]^C[6]^C[4]^C[3]^C[0]; + N[29] = D[62]^D[60]^D[55]^D[46]^D[45]^D[44]^D[43]^D[41]^D[40]^D[35]^D[33]^D[32]^D[30]^D[28]^D[25]^D[23]^D[22]^D[13]^D[8]^D[7]^D[6]^D[5]^D[4]^D[3]^D[1]^D[0] + ^C[31]^C[28]^C[27]^C[18]^C[11]^C[8]^C[6]^C[4]^C[2]^C[1]^C[0]; + N[30] = D[63]^D[62]^D[59]^D[58]^D[55]^D[52]^D[47]^D[44]^D[36]^D[35]^D[34]^D[31]^D[29]^D[22]^D[21]^D[20]^D[19]^D[15]^D[14]^D[10]^D[6]^D[3]^D[2]^D[0] + ^C[28]^C[25]^C[24]^C[22]^C[20]^C[15]^C[14]^C[12]^C[10]^C[9]^C[4]^C[0]; + N[31] = D[61]^D[58]^D[56]^D[55]^D[54]^D[52]^D[51]^D[50]^D[49]^D[42]^D[38]^D[37]^D[36]^D[34]^D[31]^D[30]^D[27]^D[26]^D[23]^D[22]^D[21]^D[19]^D[18]^D[12]^D[0] + ^C[28]^C[26]^C[24]^C[21]^C[17]^C[16]^C[14]^C[13]^C[10]^C[8]^C[2]; + newcrc = N; + end + endfunction +endmodule diff --git a/test_regress/t/t_func_dotted.v b/test_regress/t/t_func_dotted.v new file mode 100644 index 000000000..ddb46375c --- /dev/null +++ b/test_regress/t/t_func_dotted.v @@ -0,0 +1,115 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2006 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + // verilator lint_off MULTIDRIVEN + + ma ma0 (); + + global_mod #(32'hf00d) global_cell (); + global_mod #(32'hf22d) global_cell2 (); + + input clk; + integer cyc=1; + + function [31:0] getName; input fake; getName = "t "; endfunction + + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc==2) begin + if (global_cell. getGlob(1'b0) !== 32'hf00d) $stop; + if (global_cell2.getGlob(1'b0) !== 32'hf22d) $stop; + end + if (cyc==3) begin + if (ma0. getName(1'b0) !== "ma ") $stop; + if (ma0.mb0. getName(1'b0) !== "mb ") $stop; + if (ma0.mb0.mc0.getName(1'b0) !== "mc ") $stop; + end + if (cyc==4) begin + if (ma0.mb0. getP2(1'b0) !== 32'h0) $stop; + if (ma0.mb0.mc0.getP3(1'b0) !== 32'h0) $stop; + if (ma0.mb0.mc1.getP3(1'b0) !== 32'h1) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +`ifdef USE_INLINE_MID + `define INLINE_MODULE /*verilator inline_module*/ + `define INLINE_MID_MODULE /*verilator no_inline_module*/ +`else + `ifdef USE_INLINE + `define INLINE_MODULE /*verilator inline_module*/ + `define INLINE_MID_MODULE /*verilator inline_module*/ + `else + `define INLINE_MODULE /*verilator public_module*/ + `define INLINE_MID_MODULE /*verilator public_module*/ + `endif +`endif + +module global_mod; + `INLINE_MODULE + parameter INITVAL = 0; + integer global; + + initial global = INITVAL; + function [31:0] getName; input fake; getName = "gmod"; endfunction + function [31:0] getGlob; input fake; getGlob = global; endfunction +endmodule + +module ma (); + `INLINE_MODULE + + mb #(0) mb0 (); + function [31:0] getName; input fake; getName = "ma "; endfunction + + initial begin + if (ma.getName(1'b0) !== "ma ") $stop; + if (mb0.getName(1'b0) !== "mb ") $stop; + if (mb0.mc0.getName(1'b0) !== "mc ") $stop; + end +endmodule + +module mb (); + `INLINE_MID_MODULE + parameter P2 = 0; + + mc #(P2,0) mc0 (); + mc #(P2,1) mc1 (); + global_mod #(32'hf33d) global_cell2 (); + + function [31:0] getName; input fake; getName = "mb "; endfunction + function [31:0] getP2 ; input fake; getP2 = P2; endfunction + + initial begin + if (ma.getName(1'b0) !== "ma ") $stop; + if (getName(1'b0) !== "mb ") $stop; + if (mc1.getName(1'b0) !== "mc ") $stop; + end +endmodule + +module mc (); + `INLINE_MODULE + parameter P2 = 0; + parameter P3 = 0; + + function [31:0] getName; input fake; getName = "mc "; endfunction + function [31:0] getP3 ; input fake; getP3 = P3; endfunction + + initial begin + if (ma.getName(1'b0) !== "ma ") $stop; + if (mb.getName(1'b0) !== "mb ") $stop; + if (mc.getName(1'b0) !== "mc ") $stop; + end +endmodule diff --git a/test_regress/t/t_func_dotted_inl0.pl b/test_regress/t/t_func_dotted_inl0.pl new file mode 100755 index 000000000..42b452611 --- /dev/null +++ b/test_regress/t/t_func_dotted_inl0.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +top_filename("t/t_func_dotted.v"); + +compile ( + v_flags2 => ['+define+NOUSE_INLINE',], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_dotted_inl1.pl b/test_regress/t/t_func_dotted_inl1.pl new file mode 100755 index 000000000..f765b9aa0 --- /dev/null +++ b/test_regress/t/t_func_dotted_inl1.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +top_filename("t/t_func_dotted.v"); + +compile ( + v_flags2 => ['+define+USE_INLINE',], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_dotted_inl2.pl b/test_regress/t/t_func_dotted_inl2.pl new file mode 100755 index 000000000..f9c0ef3a0 --- /dev/null +++ b/test_regress/t/t_func_dotted_inl2.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +top_filename("t/t_func_dotted.v"); + +compile ( + v_flags2 => ['+define+USE_INLINE_MID',], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_flip.pl b/test_regress/t/t_func_flip.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_func_flip.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_flip.v b/test_regress/t/t_func_flip.v new file mode 100644 index 000000000..55fa39689 --- /dev/null +++ b/test_regress/t/t_func_flip.v @@ -0,0 +1,54 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +`define INT_RANGE 31:0 +`define INT_RANGE 31:0 // Duplicate identical defs are OK +`define INT_RANGE_MAX 31 +`define VECTOR_RANGE 511:0 + +module t (clk); + + // verilator lint_off WIDTH + + parameter WIDTH = 16; // Must be a power of 2 + parameter WIDTH_LOG2 = 4; // set to log2(WIDTH) + parameter USE_BS = 1; // set to 1 for enable + + input clk; + + function [`VECTOR_RANGE] func_tree_left; + input [`VECTOR_RANGE] x; // x[width-1:0] is the input vector + reg [`VECTOR_RANGE] flip; + begin + flip = 'd0; + func_tree_left = flip; + end + endfunction + + reg [WIDTH-1:0] a; // value to be shifted + reg [WIDTH-1:0] tree_left; + always @(a) begin : barrel_shift + tree_left = func_tree_left (a); + end // barrel_shift + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + a = 5; + end + if (cyc==2) begin + $display ("%x\n",tree_left); + //if (tree_left != 'd15) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + + +endmodule diff --git a/test_regress/t/t_func_numones.pl b/test_regress/t/t_func_numones.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_func_numones.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_numones.v b/test_regress/t/t_func_numones.v new file mode 100644 index 000000000..5a86d5470 --- /dev/null +++ b/test_regress/t/t_func_numones.v @@ -0,0 +1,50 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (clk); + input clk; + + + reg [31:0] r32; + wire [3:0] w4; + wire [4:0] w5; + + assign w4 = NUMONES_8 ( r32[7:0] ); + assign w5 = NUMONES_16( r32[15:0] ); + + function [3:0] NUMONES_8; + input [7:0] i8; + reg [7:0] i8; + begin + NUMONES_8 = 4'b1; + end + endfunction // NUMONES_8 + + function [4:0] NUMONES_16; + input [15:0] i16; + reg [15:0] i16; + begin + NUMONES_16 = ( NUMONES_8( i16[7:0] ) + NUMONES_8( i16[15:8] )); + end + endfunction + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + r32 <= 32'h12345678; + end + if (cyc==2) begin + if (w4 !== 1) $stop; + if (w5 !== 2) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_func_outp.pl b/test_regress/t/t_func_outp.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_func_outp.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_outp.v b/test_regress/t/t_func_outp.v new file mode 100644 index 000000000..8d17993ac --- /dev/null +++ b/test_regress/t/t_func_outp.v @@ -0,0 +1,96 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (clk); + + input clk; + + reg [7:0] a,b; + wire [7:0] z; + + mytop u0 ( a, b, clk, z ); + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + //$write("%d %x\n", cyc, z); + if (cyc==1) begin + a <= 8'h07; + b <= 8'h20; + end + if (cyc==2) begin + a <= 8'h8a; + b <= 8'h12; + end + if (cyc==3) begin + if (z !== 8'hdf) $stop; + a <= 8'h71; + b <= 8'hb2; + end + if (cyc==4) begin + if (z !== 8'hed) $stop; + end + if (cyc==5) begin + if (z !== 8'h4d) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule // mytop + +module inv( + input [ 7:0 ] a, + output [ 7:0 ] z + ); + wire [7:0] z = ~a; +endmodule + + +module ftest( + input [ 7:0 ] a, + input [ 7:0 ] b, + input clk, + output [ 7:0 ] z + ); + + wire [7:0] zi; + reg [7:0] z; + + inv u1 (.a(myadd(a,b)), + .z(zi)); + + + always @ ( posedge clk ) begin + z <= myadd( a, zi ); + end + + function [ 7:0 ] myadd; + input [7:0] ina; + input [7:0] inb; + + begin + myadd = ina + inb; + end + endfunction // myadd + +endmodule // ftest + +module mytop ( + input [ 7:0 ] a, + input [ 7:0 ] b, + input clk, + output [ 7:0 ] z + ); + + ftest u0( a, b, clk, z ); + +endmodule // mytop + diff --git a/test_regress/t/t_func_paramed.pl b/test_regress/t/t_func_paramed.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_func_paramed.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_paramed.v b/test_regress/t/t_func_paramed.v new file mode 100644 index 000000000..21cdc05c6 --- /dev/null +++ b/test_regress/t/t_func_paramed.v @@ -0,0 +1,80 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [11:0] in_a; + reg [31:0] sel; + wire [2:0] out_x; + + extractor #(4,3) extractor ( + // Outputs + .out (out_x), + // Inputs + .in (in_a), + .sel (sel)); + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + //$write("%d %x %x %x\n", cyc, in_a, sel, out_x); + if (cyc==1) begin + in_a <= 12'b001_101_111_010; + sel <= 32'd0; + end + if (cyc==2) begin + sel <= 32'd1; + if (out_x != 3'b010) $stop; + end + if (cyc==3) begin + sel <= 32'd2; + if (out_x != 3'b111) $stop; + end + if (cyc==4) begin + sel <= 32'd3; + if (out_x != 3'b101) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule + +module extractor (/*AUTOARG*/ + // Outputs + out, + // Inputs + in, sel + ); + parameter IN_WIDTH=8; + parameter OUT_WIDTH=2; + + input [IN_WIDTH*OUT_WIDTH-1:0] in; + output [OUT_WIDTH-1:0] out; + input [31:0] sel; + + wire [OUT_WIDTH-1:0] out = selector(in,sel); + + function [OUT_WIDTH-1:0] selector; + input [IN_WIDTH*OUT_WIDTH-1:0] inv; + input [31:0] selv; + integer i; + begin + selector = 0; + for (i=0; i [$Last_Self->{v3}?'+define+VERILATOR_PUBLIC_TASKS':''], + fails => $fail, + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_public.v b/test_regress/t/t_func_public.v new file mode 100644 index 000000000..2f11e985a --- /dev/null +++ b/test_regress/t/t_func_public.v @@ -0,0 +1,229 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (clk); + input clk; + + tpub p1 (.clk(clk), .i(32'd1)); + tpub p2 (.clk(clk), .i(32'd2)); + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin +`ifdef verilator + $c("publicTop();"); +`endif + end + if (cyc==20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + + task publicTop; + // verilator public + // We have different optimizations if only one of something, so try it out. + $write("Hello in publicTop\n"); + endtask + +endmodule + +module tpub ( + input clk, + input [31:0] i); + + reg [23:0] var_long; + reg [59:0] var_quad; + reg [71:0] var_wide; + reg var_bool; + + // verilator lint_off BLKANDNBLK + reg [11:0] var_flop; + // verilator lint_on BLKANDNBLK + + reg [23:0] got_long /*verilator public*/; + reg [59:0] got_quad /*verilator public*/; + reg [71:0] got_wide /*verilator public*/; + reg got_bool /*verilator public*/; + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + // cyc==1 is in top level + if (cyc==2) begin + publicNoArgs; + publicSetBool(1'b1); + publicSetLong(24'habca); + publicSetQuad(60'h4444_3333_2222); + publicSetWide(72'h12_5678_9123_1245_2352); + var_flop <= 12'habe; + end + if (cyc==3) begin + if (1'b1 != publicGetSetBool(1'b0)) $stop; + if (24'habca != publicGetSetLong(24'h1234)) $stop; + if (60'h4444_3333_2222 != publicGetSetQuad(60'h123_4567_89ab)) $stop; + if (72'h12_5678_9123_1245_2352 != publicGetSetWide(72'hac_abca_aaaa_bbbb_1234)) $stop; + end + if (cyc==4) begin + publicGetBool(got_bool); + if (1'b0 != got_bool) $stop; + publicGetLong(got_long); + if (24'h1234 != got_long) $stop; + publicGetQuad(got_quad); + if (60'h123_4567_89ab != got_quad) $stop; + publicGetWide(got_wide); + if (72'hac_abca_aaaa_bbbb_1234 != got_wide) $stop; + end + // +`ifdef VERILATOR_PUBLIC_TASKS + if (cyc==11) begin + $c("publicNoArgs();"); + $c("publicSetBool(true);"); + $c("publicSetLong(0x11bca);"); + $c("publicSetQuad(0x66655554444);"); + $c("publicSetFlop(0x321);"); + //Unsupported: $c("WData w[3] = {0x12, 0x5678_9123, 0x1245_2352}; publicSetWide(w);"); + end + if (cyc==12) begin + $c("got_bool = publicGetSetBool(true);"); + $c("got_long = publicGetSetLong(0x11bca);"); + $c("got_quad = publicGetSetQuad(0xaaaabbbbcccc);"); + end + if (cyc==13) begin + $c("{ bool gb; publicGetBool(gb); got_bool=gb; }"); + if (1'b1 != got_bool) $stop; + $c("publicGetLong(got_long);"); + if (24'h11bca != got_long) $stop; + $c("{ uint64_t qq; publicGetQuad(qq); got_quad=qq; }"); + if (60'haaaa_bbbb_cccc != got_quad) $stop; + //Unsupported: $c("publicGetWide(got_wide);"); + //Unsupported: if (72'hac_abca_aaaa_bbbb_1234 != got_wide) $stop; + //Below doesn't work, because we're calling it inside the loop that sets var_flop + // if (12'h321 != var_flop) $stop; + end + if (cyc==14) begin + if ($c32("publicInstNum()") != i) $stop; + end +`endif + end + end + + task publicEmpty; + // verilator public + begin end + endtask + + task publicNoArgs; + // verilator public + $write("Hello in publicNoArgs\n"); + endtask + + task publicSetBool; + // verilator public + input in_bool; + var_bool = in_bool; + endtask + + task publicSetLong; + // verilator public + input [23:0] in_long; + reg [23:0] not_long; + begin + not_long = ~in_long; // Test that we can have local variables + var_long = ~not_long; + end + endtask + + task publicSetQuad; + // verilator public + input [59:0] in_quad; + var_quad = in_quad; + endtask + + task publicSetFlop; + // verilator public + input [11:0] in_flop; + var_flop = in_flop; + endtask + + task publicSetWide; + // Can't be public, as no wide return types in C++ + input [71:0] in_wide; + var_wide = in_wide; + endtask + + task publicGetBool; + // verilator public + output out_bool; + out_bool = var_bool; + endtask + + task publicGetLong; + // verilator public + output [23:0] out_long; + out_long = var_long; + endtask + + task publicGetQuad; + // verilator public + output [59:0] out_quad; + out_quad = var_quad; + endtask + + task publicGetWide; + // Can't be public, as no wide return types in C++ + output [71:0] out_wide; + out_wide = var_wide; + endtask + + function publicGetSetBool; + // verilator public + input in_bool; + begin + publicGetSetBool = var_bool; + var_bool = in_bool; + end + endfunction + + function [23:0] publicGetSetLong; + // verilator public + input [23:0] in_long; + begin + publicGetSetLong = var_long; + var_long = in_long; + end + endfunction + + function [59:0] publicGetSetQuad; + // verilator public + input [59:0] in_quad; + begin + publicGetSetQuad = var_quad; + var_quad = in_quad; + end + endfunction + + function [71:0] publicGetSetWide; + // Can't be public, as no wide return types in C++ + input [71:0] in_wide; + begin + publicGetSetWide = var_wide; + var_wide = in_wide; + end + endfunction + +`ifdef VERILATOR_PUBLIC_TASKS + function [31:0] publicInstNum; + // verilator public + publicInstNum = i; + endfunction +`endif + +endmodule diff --git a/test_regress/t/t_func_public_trace.pl b/test_regress/t/t_func_public_trace.pl new file mode 100755 index 000000000..c42a59b04 --- /dev/null +++ b/test_regress/t/t_func_public_trace.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +my $pubtask = ($Last_Self->{v3} && verilator_version() =~ /\(public_tasks\)/); # TBD + +top_filename("t/t_func_public.v"); + +compile ( + v_flags2 => [($pubtask?'-DVERILATOR_PUBLIC_TASKS':''), "--trace"], + fails => $fail, + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_rand.cpp b/test_regress/t/t_func_rand.cpp new file mode 100644 index 000000000..1a0daa8e6 --- /dev/null +++ b/test_regress/t/t_func_rand.cpp @@ -0,0 +1,26 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2006 by Wilson Snyder. + +#include +#include "Vt_func_rand.h" + +int main (int argc, char *argv[]) { + Vt_func_rand *topp = new Vt_func_rand; + + Verilated::debug(0); + + printf ("\nTesting\n"); + for (int i = 0; i < 10; i++) { + topp->clk = 0; + topp->eval(); + topp->clk = 1; + topp->eval(); + } + if (topp->Rand != 0xfeed0fad) { + vl_fatal(__FILE__,__LINE__,"top", "Unexpected value for Rand output\n"); + } + printf ("*-* All Finished *-*\n"); +} diff --git a/test_regress/t/t_func_rand.pl b/test_regress/t/t_func_rand.pl new file mode 100755 index 000000000..db1567f01 --- /dev/null +++ b/test_regress/t/t_func_rand.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +compile ( + make_top_shell => 0, + make_main => 0, + v_flags2 => ["--exe t/$Last_Self->{name}.cpp"], + ) if $Last_Self->{v3}; + +execute ( + check_finished=>1, + ) if $Last_Self->{v3}; + +ok(1); +1; diff --git a/test_regress/t/t_func_rand.v b/test_regress/t/t_func_rand.v new file mode 100644 index 000000000..580c1d063 --- /dev/null +++ b/test_regress/t/t_func_rand.v @@ -0,0 +1,33 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2006 by Wilson Snyder. + +module t (clk, Rand); + input clk; + output reg [31:0] Rand; + +`ifdef verilator + `systemc_interface + unsigned int QxRandTbl (unsigned int tbl, unsigned int idx) { return 0xfeed0fad; } + `verilog +`endif + + function [31:0] QxRand32; + /* verilator public */ + input [7:0] tbl; + input [7:0] idx; + begin +`ifdef verilator + QxRand32 = $c ("QxRandTbl(",tbl,",",idx,")"); +`else + QxRand32 = 32'hfeed0fad; +`endif + end + endfunction + + always @(posedge clk) begin + Rand <= #1 QxRand32 (8'h0, 8'h7); + end +endmodule diff --git a/test_regress/t/t_func_range.pl b/test_regress/t/t_func_range.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_func_range.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_range.v b/test_regress/t/t_func_range.v new file mode 100644 index 000000000..7ccb61fe9 --- /dev/null +++ b/test_regress/t/t_func_range.v @@ -0,0 +1,64 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (clk); + input clk; + + // verilator lint_off WIDTH + +`define INT_RANGE 31:0 +`define INT_RANGE_MAX 31 +`define VECTOR_RANGE 63:0 + + reg [`INT_RANGE] stashb, stasha, stashn, stashm; + + function [`VECTOR_RANGE] copy_range; + input [`VECTOR_RANGE] y; + input [`INT_RANGE] b; + input [`INT_RANGE] a; + + input [`VECTOR_RANGE] x; + input [`INT_RANGE] n; + input [`INT_RANGE] m; + + begin + copy_range = y; + stashb = b; + stasha = a; + stashn = n; + stashm = m; + end + endfunction + + parameter DATA_SIZE = 16; + parameter NUM_OF_REGS = 32; + + reg [NUM_OF_REGS*DATA_SIZE-1 : 0] memread_rf; + reg [DATA_SIZE-1:0] memread_rf_reg; + always @(memread_rf) begin : memread_convert + memread_rf_reg = copy_range('d0, DATA_SIZE-'d1, DATA_SIZE-'d1, memread_rf, + DATA_SIZE-'d1, DATA_SIZE-'d1); + end + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + memread_rf = 512'haa; + end + if (cyc==3) begin + if (stashb != 'd15) $stop; + if (stasha != 'd15) $stop; + if (stashn != 'd15) $stop; + if (stashm != 'd15) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_func_regfirst.pl b/test_regress/t/t_func_regfirst.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_func_regfirst.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_regfirst.v b/test_regress/t/t_func_regfirst.v new file mode 100644 index 000000000..97761db0a --- /dev/null +++ b/test_regress/t/t_func_regfirst.v @@ -0,0 +1,78 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (clk); + input clk; + + reg [2:0] a; + reg [2:0] b; + reg q; + + f6 f6 (/*AUTOINST*/ + // Outputs + .q (q), + // Inputs + .a (a[2:0]), + .b (b[2:0]), + .clk (clk)); + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + a <= 3'b000; + b <= 3'b100; + end + if (cyc==2) begin + a <= 3'b011; + b <= 3'b001; + if (q != 1'b0) $stop; + end + if (cyc==3) begin + a <= 3'b011; + b <= 3'b011; + if (q != 1'b0) $stop; + end + if (cyc==9) begin + if (q != 1'b1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +module f6 (a, b, clk, q); + input [2:0] a; + input [2:0] b; + input clk; + output q; + reg out; + + function func6; + reg result; + input [5:0] src; + begin + if (src[5:0] == 6'b011011) begin + result = 1'b1; + end + else begin + result = 1'b0; + end + func6 = result; + end + endfunction + + wire [5:0] w6 = {a, b}; + always @(posedge clk) begin + out <= func6(w6); + end + + assign q = out; + +endmodule diff --git a/test_regress/t/t_func_twocall.pl b/test_regress/t/t_func_twocall.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_func_twocall.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_twocall.v b/test_regress/t/t_func_twocall.v new file mode 100644 index 000000000..d2b8b680d --- /dev/null +++ b/test_regress/t/t_func_twocall.v @@ -0,0 +1,61 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (clk); + input clk; + + reg [7:0] crc; + + wire [61:59] ah = crc[5:3]; + wire [61:59] bh = ~crc[4:2]; + wire [41:2] al = {crc,crc,crc,crc,crc}; + wire [41:2] bl = ~{crc[6:0],crc[6:0],crc[6:0],crc[6:0],crc[6:0],crc[6:2]}; + reg sel; + + wire [61:28] q = ( sel + ? func(ah, al) + : func(bh, bl)); + + function [61:28] func; + input [61:59] inh; + input [41:2] inl; + reg [42:28] func_mid; + reg carry; + begin + carry = &inl[27:2]; + func_mid = {1'b0,inl[41:28]} + {14'b0, carry}; + func[61:59] = inh + {2'b0, func_mid[42]}; + func[58:42] = {17{func_mid[41]}}; + func[41:28] = func_mid[41:28]; + end + endfunction + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + //$write("%d %x\n", cyc, q); + if (cyc!=0) begin + cyc <= cyc + 1; + sel <= ~sel; + crc <= {crc[6:0], ~^ {crc[7],crc[5],crc[4],crc[3]}}; + if (cyc==1) begin + sel <= 1'b1; + crc <= 8'h12; + end + if (cyc==2) if (q!=34'h100000484) $stop; + if (cyc==3) if (q!=34'h37fffeddb) $stop; + if (cyc==4) if (q!=34'h080001212) $stop; + if (cyc==5) if (q!=34'h1fffff7ef) $stop; + if (cyc==6) if (q!=34'h200000848) $stop; + if (cyc==7) if (q!=34'h380001ebd) $stop; + if (cyc==8) if (q!=34'h07fffe161) $stop; + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_func_wide.pl b/test_regress/t/t_func_wide.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_func_wide.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_func_wide.v b/test_regress/t/t_func_wide.v new file mode 100644 index 000000000..54505d0bc --- /dev/null +++ b/test_regress/t/t_func_wide.v @@ -0,0 +1,45 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (clk); + input clk; + + reg [43:0] mi; + wire [31:0] mo; + muxtop um ( mi, mo); + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + mi <= 44'h1234567890; + end + if (cyc==3) begin + if (mo !== 32'h12345678) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +module muxtop ( + input [ 43:0 ] i, + output reg [ 31:0 ] o + ); + + always @ ( i ) + o = MUX( i[39:0] ); + + function [31:0] MUX; + input [39:0] XX ; + begin + MUX = XX[39:8]; + end + endfunction +endmodule diff --git a/test_regress/t/t_gate_basic.pl b/test_regress/t/t_gate_basic.pl new file mode 100755 index 000000000..eff491d7a --- /dev/null +++ b/test_regress/t/t_gate_basic.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_gate_basic.v b/test_regress/t/t_gate_basic.v new file mode 100644 index 000000000..c70f0f76b --- /dev/null +++ b/test_regress/t/t_gate_basic.v @@ -0,0 +1,87 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [31:0] a; + reg [31:0] b; + + wire [1:0] bf; buf BF0 (bf[0], a[0]), + BF1 (bf[1], a[1]); + + // verilator lint_off IMPLICIT + not NT0 (nt0, a[0]); + and AN0 (an0, a[0], b[0]); + nand ND0 (nd0, a[0], b[0], b[1]); + or OR0 (or0, a[0], b[0]); + nor NR0 (nr0, a[0], b[0], b[2]); + xor (xo0, a[0], b[0]); + xnor (xn0, a[0], b[0], b[2]); + // verilator lint_on IMPLICIT + +`ifdef verilator + specify + specparam CDS_LIBNAME = "foobar"; + (nt0 *> nt0) = (0, 0); + endspecify + + specify + // delay parameters + specparam + a$A1$Y = 1.0, + b$A0$Z = 1.0; + + // path delays + (A1 *> Q) = (a$A1$Y, a$A1$Y); + (A0 *> Q) = (b$A0$Y, a$A0$Z); + endspecify +`endif + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + a <= 32'h18f6b034; + b <= 32'h834bf892; + end + if (cyc==2) begin + a <= 32'h529ab56f; + b <= 32'h7835a237; + if (bf[0] !== 1'b0) $stop; + if (bf[1] !== 1'b0) $stop; + if (nt0 !== 1'b1) $stop; + if (an0 !== 1'b0) $stop; + if (nd0 !== 1'b1) $stop; + if (or0 !== 1'b0) $stop; + if (nr0 !== 1'b1) $stop; + if (xo0 !== 1'b0) $stop; + if (xn0 !== 1'b1) $stop; + end + if (cyc==3) begin + if (bf[0] !== 1'b1) $stop; + if (bf[1] !== 1'b1) $stop; + if (nt0 !== 1'b0) $stop; + if (an0 !== 1'b1) $stop; + if (nd0 !== 1'b0) $stop; + if (or0 !== 1'b1) $stop; + if (nr0 !== 1'b0) $stop; + if (xo0 !== 1'b0) $stop; + if (xn0 !== 1'b0) $stop; + end + if (cyc==4) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_gate_elim.pl b/test_regress/t/t_gate_elim.pl new file mode 100755 index 000000000..c4aacfed8 --- /dev/null +++ b/test_regress/t/t_gate_elim.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_gate_elim.v b/test_regress/t/t_gate_elim.v new file mode 100644 index 000000000..2cc7f1023 --- /dev/null +++ b/test_regress/t/t_gate_elim.v @@ -0,0 +1,121 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg b; + + wire vconst1 = 1'b0; + wire vconst2 = !(vconst1); + wire vconst3 = !vconst2; + wire vconst = vconst3; + + wire qa; + wire qb; + wire qc; + wire qd; + wire qe; + ta ta (.b(b), .vconst(vconst), .q(qa)); + tb tb (.clk(clk), .vconst(vconst), .q(qb)); + tc tc (.b(b), .vconst(vconst), .q(qc)); + td td (.b(b), .vconst(vconst), .q(qd)); + te te (.clk(clk), .b(b), .vconst(vconst), .q(qe)); + + always @ (posedge clk) begin + $display("%b",{qa,qb,qc,qd,qe}); + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + b <= 1'b1; + end + if (cyc==2) begin + if (qa!=1'b1) $stop; + if (qb!=1'b0) $stop; + if (qd!=1'b0) $stop; + b <= 1'b0; + end + if (cyc==3) begin + if (qa!=1'b0) $stop; + if (qb!=1'b0) $stop; + if (qd!=1'b0) $stop; + if (qe!=1'b0) $stop; + b <= 1'b1; + end + if (cyc==4) begin + if (qa!=1'b1) $stop; + if (qb!=1'b0) $stop; + if (qd!=1'b0) $stop; + if (qe!=1'b1) $stop; + b <= 1'b0; + end + if (cyc==5) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule + +module ta ( + input vconst, + input b, + output reg q); + + always @ (/*AS*/b or vconst) begin + q = vconst | b; + end +endmodule + +module tb ( + input vconst, + input clk, + output reg q); + + always @ (posedge clk) begin + q <= vconst; + end +endmodule + +module tc ( + input vconst, + input b, + output reg q); + always @ (posedge vconst) begin + q <= b; + $stop; + end +endmodule + +module td ( + input vconst, + input b, + output reg q); + + always @ (/*AS*/vconst) begin + q = vconst; + end +endmodule + +module te ( + input clk, + input vconst, + input b, + output reg q); + reg qmid; + always @ (posedge vconst or posedge clk) begin + qmid <= b; + end + always @ (posedge clk or posedge vconst) begin + q <= qmid; + end +endmodule + diff --git a/test_regress/t/t_gen_assign.pl b/test_regress/t/t_gen_assign.pl new file mode 100755 index 000000000..48a02a231 --- /dev/null +++ b/test_regress/t/t_gen_assign.pl @@ -0,0 +1,14 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +compile ( + ) if $Last_Self->{v3}; + +ok(1); +1; diff --git a/test_regress/t/t_gen_assign.v b/test_regress/t/t_gen_assign.v new file mode 100644 index 000000000..f6dfb1fcb --- /dev/null +++ b/test_regress/t/t_gen_assign.v @@ -0,0 +1,58 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// This file ONLY is placed into the Public Domain, for any use, +// without warranty. +`timescale 1ns / 1ps + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; initial cyc=0; + reg [63:0] crc; + reg [31:0] sum; + + wire [8:0] Output; + wire [8:0] Input = crc[8:0]; + + assigns assigns (/*AUTOINST*/ + // Outputs + .Output (Output[8:0]), + // Inputs + .Input (Input[8:0])); + + always @ (posedge clk) begin + $write("[%0t] cyc==%0d crc=%x q=%x\n",$time, cyc, crc, sum); + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63]^crc[2]^crc[0]}; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + sum <= 32'h0; + end + else if (cyc>10 && cyc<90) begin + sum <= {sum[30:0],sum[31]} ^ {23'h0, crc[8:0]}; + end + else if (cyc==99) begin + if (sum != 32'he8bbd130) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module assigns(Input, Output); + input [8:0] Input; + output [8:0] Output; + + genvar i; + generate + for (i = 0; i < 8; i = i + 1) begin : ap + assign Output[(i>0) ? i-1 : 8] = Input[(i>0) ? i-1 : 8]; + end + endgenerate +endmodule diff --git a/test_regress/t/t_gen_for.pl b/test_regress/t/t_gen_for.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_gen_for.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_gen_for.v b/test_regress/t/t_gen_for.v new file mode 100644 index 000000000..97df26ff4 --- /dev/null +++ b/test_regress/t/t_gen_for.v @@ -0,0 +1,166 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + integer cyc=0; + + reg [7:0] crc; + genvar g; + + wire [7:0] out_p1; + wire [15:0] out_p2; + wire [7:0] out_p3; + wire [7:0] out_p4; + + paramed #(.WIDTH(8), .MODE(0)) p1 (.in(crc), .out(out_p1)); + paramed #(.WIDTH(16), .MODE(1)) p2 (.in({crc,crc}), .out(out_p2)); + paramed #(.WIDTH(8), .MODE(2)) p3 (.in(crc), .out(out_p3)); + gencase #(.MODE(3)) p4 (.in(crc), .out(out_p4)); + + wire [7:0] out_ef; + enflop #(.WIDTH(8)) enf (.a(crc), .q(out_ef), .oe_e1(1'b1), .clk(clk)); + + always @ (posedge clk) begin + //$write("[%0t] cyc==%0d crc=%b %x %x %x %x %x\n",$time, cyc, crc, out_p1, out_p2, out_p3, out_p4, out_ef); + cyc <= cyc + 1; + crc <= {crc[6:0], ~^ {crc[7],crc[5],crc[4],crc[3]}}; + if (cyc==0) begin + // Setup + crc <= 8'hed; + end + else if (cyc==1) begin + end + else if (cyc==3) begin + if (out_p1 !== 8'h2d) $stop; + if (out_p2 !== 16'h2d2d) $stop; + if (out_p3 !== 8'h78) $stop; + if (out_p4 !== 8'h44) $stop; + if (out_ef !== 8'hda) $stop; + end + else if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module gencase (/*AUTOARG*/ + // Outputs + out, + // Inputs + in + ); + parameter MODE = 0; + input [7:0] in; + output [7:0] out; + generate // : genblk1 + begin + case (MODE) + 2: mbuf mc [7:0] (.q(out[7:0]), .a({in[5:0],in[7:6]})); + default: mbuf mc [7:0] (.q(out[7:0]), .a({in[3:0],in[3:0]})); + endcase + end + endgenerate + +endmodule + +module paramed (/*AUTOARG*/ + // Outputs + out, + // Inputs + in + ); + parameter WIDTH = 1; + parameter MODE = 0; + input [WIDTH-1:0] in; + output [WIDTH-1:0] out; + + generate + if (MODE==0) initial $write("Mode=0\n"); + // No else + endgenerate + + generate + genvar i; + // Empty loop body + for (i=0; i<3; i=i+1) begin end + endgenerate + + generate + genvar i; + if (MODE==0) begin + // Flip bitorder, direct assign method + for (i=0; i{v3}; + +ok(1); +1; diff --git a/test_regress/t/t_gen_if.v b/test_regress/t/t_gen_if.v new file mode 100644 index 000000000..d42b54ea1 --- /dev/null +++ b/test_regress/t/t_gen_if.v @@ -0,0 +1,25 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// simplistic example, should choose 1st conditional generate and assign straight through +// the tool also compiles the special case and determines an error (replication value is 0) +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty. +`timescale 1ns / 1ps + +module t(data_i, data_o); + parameter op_bits = 32; + input [op_bits -1:0] data_i; + output [31:0] data_o; + + //simplistic example, should choose 1st conditional generate and assign straight through + //the tool also compiles the special case and determines an error (replication value is 0 + generate + if (op_bits == 32) begin : general_case + assign data_o = data_i; + end + else begin : special_case + assign data_o = {{(32 -op_bits){1'b0}},data_i}; + end + endgenerate +endmodule diff --git a/test_regress/t/t_gen_var_bad.pl b/test_regress/t/t_gen_var_bad.pl new file mode 100755 index 000000000..4c13281f4 --- /dev/null +++ b/test_regress/t/t_gen_var_bad.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>1, + expect=> +'.*%Error: t/t_gen_var_bad.v:\d+: Non-genvar used in generate for: i +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_gen_var_bad.v b/test_regress/t/t_gen_var_bad.v new file mode 100644 index 000000000..7cfb50493 --- /dev/null +++ b/test_regress/t/t_gen_var_bad.v @@ -0,0 +1,13 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t; + integer i; + generate + for (i=0; i<3; i=i+1) begin // Bad: i is not a genvar + end + endgenerate +endmodule diff --git a/test_regress/t/t_init_concat.pl b/test_regress/t/t_init_concat.pl new file mode 100755 index 000000000..eff491d7a --- /dev/null +++ b/test_regress/t/t_init_concat.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_init_concat.v b/test_regress/t/t_init_concat.v new file mode 100644 index 000000000..e3bd48946 --- /dev/null +++ b/test_regress/t/t_init_concat.v @@ -0,0 +1,91 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [31:0] wr_data; + reg wr_en; + wire [31:0] rd_data; + wire [1:0] rd_guards; + wire [1:0] rd_guardsok; + + regfile regfile (/*AUTOINST*/ + // Outputs + .rd_data (rd_data[31:0]), + .rd_guards (rd_guards[1:0]), + .rd_guardsok (rd_guardsok[1:0]), + // Inputs + .wr_data (wr_data[31:0]), + .wr_en (wr_en), + .clk (clk)); + + initial wr_en = 0; + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + if (!rd_guards[0]) $stop; + if (!rd_guardsok[0]) $stop; + wr_en <= 1'b1; + wr_data <= 32'hfeedf; + end + if (cyc==2) begin + wr_en <= 0; + end + if (cyc==3) begin + wr_en <= 0; + if (rd_data != 32'hfeedf) $stop; + if (rd_guards != 2'b11) $stop; + if (rd_guardsok != 2'b11) $stop; + end + if (cyc==4) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +module regfile ( + input [31:0] wr_data, + input wr_en, + output reg [31:0] rd_data, + output [1:0] rd_guards /*verilator public*/, + output [1:0] rd_guardsok /*verilator public*/, + input clk + ); + + always @(posedge clk) begin + if (wr_en) + begin + rd_data <= wr_data; + end + end + + // this initial statement will induce correct initialize behavior + // initial rd_guards= { 2'b11 }; + + assign rd_guards= { + rd_data[0], + 1'b1 + }; + + assign rd_guardsok[0] = 1'b1; + assign rd_guardsok[1] = rd_data[0]; + +endmodule // regfile + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_inst_array.pl b/test_regress/t/t_inst_array.pl new file mode 100755 index 000000000..115c3ed81 --- /dev/null +++ b/test_regress/t/t_inst_array.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_array.v b/test_regress/t/t_inst_array.v new file mode 100644 index 000000000..26bcbacdd --- /dev/null +++ b/test_regress/t/t_inst_array.v @@ -0,0 +1,49 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + wire [17:10] bitout; + reg [7:0] allbits; + reg [15:0] onebit; + + sub sub [7:0] (allbits, onebit, bitout); + + always @ (posedge clk) begin + //$write("%x\n", bitout); + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + allbits <= 8'hac; + onebit <= 16'hc01a; + end + if (cyc==2) begin + if (bitout !== 8'h07) $stop; + allbits <= 8'hca; + onebit <= 16'h1f01; + end + if (cyc==3) begin + if (bitout !== 8'h41) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule + +module sub (input [7:0] allbits, input [1:0] onebit, output bitout); + wire bitout = (^ onebit) ^ (^ allbits); +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_inst_array_bad.pl b/test_regress/t/t_inst_array_bad.pl new file mode 100755 index 000000000..59a5e1d79 --- /dev/null +++ b/test_regress/t/t_inst_array_bad.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +compile ( + fails=>1, + expect=> +'%Error: t/t_inst_array_bad.v:19: Port connection __pinNumber2 as part of a module instance array requires 1 or 8 bits, but connection\'s VARREF generates 9 bits. +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_array_bad.v b/test_regress/t/t_inst_array_bad.v new file mode 100644 index 000000000..942a7ed27 --- /dev/null +++ b/test_regress/t/t_inst_array_bad.v @@ -0,0 +1,34 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + wire [7:0] bitout; + reg [7:0] allbits; + reg [7:0] onebit; + reg [8:0] onebitbad; // Wrongly sized + + sub sub [7:0] (allbits, onebitbad, bitout); + + // This is ok. + wire [9:8] b; + wire [1:0] c; + sub sub2 [9:8] (allbits,b,c); + +endmodule + +module sub (input [7:0] allbits, input onebit, output bitout); + wire bitout = onebit ^ (^ allbits); +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_inst_ccall.pl b/test_regress/t/t_inst_ccall.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_inst_ccall.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_ccall.v b/test_regress/t/t_inst_ccall.v new file mode 100644 index 000000000..ccb903bbf --- /dev/null +++ b/test_regress/t/t_inst_ccall.v @@ -0,0 +1,59 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003-2006 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + // verilator lint_on GENCLK + reg [31:0] long; + reg [63:0] quad; + wire [31:0] longout; + wire [63:0] quadout; + wire [7:0] narrow = long[7:0]; + sub sub (/*AUTOINST*/ + // Outputs + .longout (longout[31:0]), + .quadout (quadout[63:0]), + // Inputs + .narrow (narrow[7:0]), + .quad (quad[63:0])); + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + long <= 32'h12345678; + quad <= 64'h12345678_abcdef12; + end + if (cyc==2) begin + if (longout !== 32'h79) $stop; + if (quadout !== 64'h12345678_abcdef13) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule + +module sub (input [7:0] narrow, input [63:0] quad, output [31:0] longout, output [63:0] quadout); + // verilator public_module +`ifdef verilator + wire [31:0] longout = $c32("(",narrow,"+1)"); + wire [63:0] quadout = $c64("(",quad,"+1)"); +`else + wire [31:0] longout = narrow + 8'd1; + wire [63:0] quadout = quad + 64'd1; +`endif +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_inst_mnpipe.pl b/test_regress/t/t_inst_mnpipe.pl new file mode 100755 index 000000000..24012256c --- /dev/null +++ b/test_regress/t/t_inst_mnpipe.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_mnpipe.v b/test_regress/t/t_inst_mnpipe.v new file mode 100644 index 000000000..3a29ee68f --- /dev/null +++ b/test_regress/t/t_inst_mnpipe.v @@ -0,0 +1,72 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=0; + + reg [7:0] crc; + reg [2:0] sum; + wire [2:0] in = crc[2:0]; + wire [2:0] out; + + MxN_pipeline pipe (in, out, clk); + + always @ (posedge clk) begin + //$write("[%0t] cyc==%0d crc=%b sum=%x\n",$time, cyc, crc, sum); + cyc <= cyc + 1; + crc <= {crc[6:0], ~^ {crc[7],crc[5],crc[4],crc[3]}}; + if (cyc==0) begin + // Setup + crc <= 8'hed; + sum <= 3'h0; + end + else if (cyc>10 && cyc<90) begin + sum <= {sum[1:0],sum[2]} ^ out; + end + else if (cyc==99) begin + if (crc !== 8'b01110000) $stop; + if (sum !== 3'h3) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule + +module dffn (q,d,clk); + parameter BITS = 1; + + input [BITS-1:0] d; + output reg [BITS-1:0] q; + input clk; + + always @ (posedge clk) begin + q <= d; + end + +endmodule + +module MxN_pipeline (in, out, clk); + parameter M=3, N=4; + + input [M-1:0] in; + output [M-1:0] out; + input clk; + + // Unsupported: Per-bit array instantiations with output connections to non-wires. + //wire [M*(N-1):1] t; + //dffn #(M) p[N:1] ({out,t},{t,in},clk); + + wire [M*(N-1):1] t; + wire [M*N:1] q; + dffn #(M) p[N:1] (q,{t,in},clk); + assign {out,t} = q; + +endmodule diff --git a/test_regress/t/t_inst_overwide.pl b/test_regress/t/t_inst_overwide.pl new file mode 100755 index 000000000..828380760 --- /dev/null +++ b/test_regress/t/t_inst_overwide.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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. + +compile ( + make_top_shell=>0, + verilator_flags=> [qw(-sp -Wno-WIDTH)], + verilator_make_gcc=>0, + ); + +#No execute () + +ok(1); +1; diff --git a/test_regress/t/t_inst_overwide.v b/test_regress/t/t_inst_overwide.v new file mode 100644 index 000000000..9b06586d3 --- /dev/null +++ b/test_regress/t/t_inst_overwide.v @@ -0,0 +1,51 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Outputs + outc_w30, outd_w73, + // Inputs + clk, ina_w1, inb_w61 + ); + + input clk; + + input ina_w1; + input [60:0] inb_w61; + output [29:0] outc_w30; + output [72:0] outd_w73; + + sub sub ( + // Outputs + .outy_w92 (outc_w30), // .large => (small) + .outz_w22 (outd_w73), // .small => (large) + // Inputs + .clk (clk), + .inw_w31 (ina_w1), // .large <= (small) + .inx_w11 (inb_w61) // .small <= (large) + ); + +endmodule + +module sub (/*AUTOARG*/ + // Outputs + outy_w92, outz_w22, + // Inputs + clk, inw_w31, inx_w11 + ); + + input clk; + input [30:0] inw_w31; + input [10:0] inx_w11; + output reg [91:0] outy_w92 /*verilator public*/; + output reg [21:0] outz_w22 /*verilator public*/; + + always @(posedge clk) begin + outy_w92 <= {inw_w31[29:0],inw_w31[29:0],inw_w31[29:0],2'b00}; + outz_w22 <= {inx_w11[10:0],inx_w11[10:0]}; + end + +endmodule // regfile diff --git a/test_regress/t/t_inst_overwide_bad.pl b/test_regress/t/t_inst_overwide_bad.pl new file mode 100755 index 000000000..b29928188 --- /dev/null +++ b/test_regress/t/t_inst_overwide_bad.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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. + +top_filename("t/t_inst_overwide.v"); + +compile ( + make_top_shell=>0, + verilator_flags=> [qw(-sp)], + verilator_make_gcc=>0, + fails=>$Last_Self->{v3}, + expect=> +'%Warning-WIDTH: t/t_inst_overwide.v:\d+: Output port connection outy_w92 expects 92 bits but connection\'s VARREF generates 30 bits. +%Warning-WIDTH: Use .* to disable this message. +%Warning-WIDTH: t/t_inst_overwide.v:\d+: Output port connection outz_w22 expects 22 bits but connection\'s VARREF generates 73 bits. +%Warning-WIDTH: t/t_inst_overwide.v:\d+: Input port connection inw_w31 expects 31 bits but connection\'s VARREF generates 1 bits. +%Warning-WIDTH: t/t_inst_overwide.v:\d+: Input port connection inx_w11 expects 11 bits but connection\'s VARREF generates 61 bits. +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_recurse_bad.pl b/test_regress/t/t_inst_recurse_bad.pl new file mode 100755 index 000000000..89513f124 --- /dev/null +++ b/test_regress/t/t_inst_recurse_bad.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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. + +compile ( + fails=>1, + expect=> +'.*%Error: t/t_inst_recurse_bad.v:18: Recursive module .module instantiates itself.: looped +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_recurse_bad.v b/test_regress/t/t_inst_recurse_bad.v new file mode 100644 index 000000000..fdf05aba4 --- /dev/null +++ b/test_regress/t/t_inst_recurse_bad.v @@ -0,0 +1,24 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + looped looped (); + +endmodule + +module looped (/*AUTOARG*/); + looped2 looped2 (); +endmodule + +module looped2 (/*AUTOARG*/); + looped looped (); +endmodule diff --git a/test_regress/t/t_inst_tree.v b/test_regress/t/t_inst_tree.v new file mode 100644 index 000000000..cba49783e --- /dev/null +++ b/test_regress/t/t_inst_tree.v @@ -0,0 +1,109 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + // verilator lint_off GENCLK + reg printclk; + // verilator lint_on GENCLK + ps ps (printclk); + + reg [7:0] a; + wire [7:0] z; + + l1 u (~a,z); + + always @ (posedge clk) begin + printclk <= 0; + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + printclk <= 1'b1; + end + if (cyc==2) begin + a <= 8'b1; + end + if (cyc==3) begin + if (z !== 8'hf8) $stop; + //if (u.u1.u1.u1.u0.PARAM !== 1) $stop; + //if (u.u1.u1.u1.u1.PARAM !== 2) $stop; + //if (u.u0.u0.u0.u0.z !== 8'hfe) $stop; + //if (u.u0.u0.u0.u1.z !== 8'hff) $stop; + //if (u.u1.u1.u1.u0.z !== 8'h00) $stop; + //if (u.u1.u1.u1.u1.z !== 8'h01) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +`ifdef USE_INLINE + `define INLINE_MODULE /*verilator inline_module*/ +`else + `define INLINE_MODULE /*verilator public_module*/ +`endif + +`ifdef USE_PUBLIC + `define PUBLIC /*verilator public*/ +`else + `define PUBLIC +`endif + +module ps (input printclk); + `INLINE_MODULE + // Check that %m stays correct across inlines + always @ (posedge printclk) $write("[%0t] %m: Clocked\n", $time); +endmodule + +module l1 (input [7:0] a, output [7:0] z); + `INLINE_MODULE + wire [7:0] z0 `PUBLIC; wire [7:0] z1 `PUBLIC; + wire [7:0] z `PUBLIC; assign z = z0+z1; + l2 u0 (a, z0); l2 u1 (a, z1); +endmodule + +module l2 (input [7:0] a, output [7:0] z); + `INLINE_MODULE + wire [7:0] z0 `PUBLIC; wire [7:0] z1 `PUBLIC; + wire [7:0] z `PUBLIC; assign z = z0+z1; + wire [7:0] a1 = a+8'd1; + l3 u0 (a, z0); l3 u1 (a1, z1); +endmodule + +module l3 (input [7:0] a, output [7:0] z); + `INLINE_MODULE + wire [7:0] z0 `PUBLIC; wire [7:0] z1 `PUBLIC; + wire [7:0] z `PUBLIC; assign z = z0+z1; + wire [7:0] a1 = a+8'd1; + l4 u0 (a, z0); l4 u1 (a1, z1); +endmodule + +module l4 (input [7:0] a, output [7:0] z); + `INLINE_MODULE + wire [7:0] z0 `PUBLIC; wire [7:0] z1 `PUBLIC; + wire [7:0] z `PUBLIC; assign z = z0+z1; + wire [7:0] a1 = a+8'd1; + l5 #(1) u0 (a, z0); l5 #(2) u1 (a1, z1); +endmodule + +module l5 (input [7:0] a, output [7:0] z); + `INLINE_MODULE + parameter PARAM = 5; + wire [7:0] z0 `PUBLIC; wire [7:0] z1 `PUBLIC; + wire [7:0] z `PUBLIC; assign z = a; +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_inst_tree_inl0_pub0.pl b/test_regress/t/t_inst_tree_inl0_pub0.pl new file mode 100755 index 000000000..45149f2b1 --- /dev/null +++ b/test_regress/t/t_inst_tree_inl0_pub0.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +top_filename("t/t_inst_tree.v"); + +compile ( + v_flags2 => ['+define+NOUSE_INLINE', '+define+NOUSE_PUBLIC'], + ); + +execute ( + check_finished=>1, + expect=> +'\] (%m|.*v\.ps): Clocked +', + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_tree_inl0_pub1.pl b/test_regress/t/t_inst_tree_inl0_pub1.pl new file mode 100755 index 000000000..4682a2ab5 --- /dev/null +++ b/test_regress/t/t_inst_tree_inl0_pub1.pl @@ -0,0 +1,28 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +top_filename("t/t_inst_tree.v"); + +compile ( + v_flags2 => ['+define+NOUSE_INLINE', '+define+USE_PUBLIC', '--stats'], + ); + +if ($Last_Self->{v3}) { + file_grep ($Last_Self->{stats}, qr/Optimizations, Combined CFuncs\s+16/i); +} + +execute ( + check_finished=>1, + expect=> +'\] (%m|.*v\.ps): Clocked +', + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_tree_inl1_pub0.pl b/test_regress/t/t_inst_tree_inl1_pub0.pl new file mode 100755 index 000000000..a92046c4d --- /dev/null +++ b/test_regress/t/t_inst_tree_inl1_pub0.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +top_filename("t/t_inst_tree.v"); + +compile ( + v_flags2 => ['+define+USE_INLINE', '+define+NOUSE_PUBLIC'], + ); + +execute ( + check_finished=>1, + expect=> +'\] (%m|.*v\.ps): Clocked +', + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_tree_inl1_pub1.pl b/test_regress/t/t_inst_tree_inl1_pub1.pl new file mode 100755 index 000000000..1afc4b8f9 --- /dev/null +++ b/test_regress/t/t_inst_tree_inl1_pub1.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +top_filename("t/t_inst_tree.v"); + +compile ( + v_flags2 => ['+define+USE_INLINE', '+define+USE_PUBLIC'], + ); + +execute ( + check_finished=>1, + expect=> +'\] (%m|.*v\.ps): Clocked +', + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_v2k.pl b/test_regress/t/t_inst_v2k.pl new file mode 100755 index 000000000..9bee77438 --- /dev/null +++ b/test_regress/t/t_inst_v2k.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + v_flags2 => ['+libext+.vi+.extranoneed'], + nc => 0, # Error: Multiple +libext flags found + vcs => 0, + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_v2k.v b/test_regress/t/t_inst_v2k.v new file mode 100644 index 000000000..b29690184 --- /dev/null +++ b/test_regress/t/t_inst_v2k.v @@ -0,0 +1,60 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + supply0 [1:0] low; + supply1 [1:0] high; + + reg [7:0] isizedwire; + reg ionewire; + +`ifdef never_just_for_verilog_mode + wire oonewire; // From sub of t_inst_v2k_sub.v +`endif + + wire [7:0] osizedreg; // From sub of t_inst_v2k_sub.v + + t_inst_v2k_sub sub + ( + // Outputs + .osizedreg (osizedreg[7:0]), + // verilator lint_off IMPLICIT + .oonewire (oonewire), + // verilator lint_on IMPLICIT + // Inputs + .isizedwire (isizedwire[7:0]), + .ionewire (ionewire)); + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + ionewire <= 1'b1; + isizedwire <= 8'd8; + end + if (cyc==2) begin + if (low != 2'b00) $stop; + if (high != 2'b11) $stop; + if (oonewire !== 1'b1) $stop; + if (isizedwire !== 8'd8) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_inst_v2k_sub.vi b/test_regress/t/t_inst_v2k_sub.vi new file mode 100644 index 000000000..011cdc22a --- /dev/null +++ b/test_regress/t/t_inst_v2k_sub.vi @@ -0,0 +1,22 @@ +// $Id:$ -*- Verilog -*- +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +// This file is named .vi to test +libext+ flags. +module t_inst_v2k_sub + ( + output reg [7:0] osizedreg, + output wire oonewire /*verilator public*/, + input [7:0] isizedwire, + input wire ionewire + ); + + assign oonewire = ionewire; + + always @* begin + osizedreg = isizedwire; + end + +endmodule diff --git a/test_regress/t/t_inst_wideconst.pl b/test_regress/t/t_inst_wideconst.pl new file mode 100755 index 000000000..3c1520a21 --- /dev/null +++ b/test_regress/t/t_inst_wideconst.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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. + +compile ( + v_flags2 => ['-public'], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_inst_wideconst.v b/test_regress/t/t_inst_wideconst.v new file mode 100644 index 000000000..a5da64d65 --- /dev/null +++ b/test_regress/t/t_inst_wideconst.v @@ -0,0 +1,73 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [41:0] aaa; + wire [41:0] bbb; + + // verilator public_module + wire [41:0] z_0; + wire [41:0] z_1; + + wide w_0( + .xxx( { {40{1'b0}},2'b11 } ), + .yyy( aaa[1:0] ), + .zzz( z_0 ) + ); + + wide w_1( + .xxx( aaa ), + .yyy( 2'b10 ), + .zzz( z_1 ) + ); + + assign bbb= z_0 + z_1; + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + aaa <= 42'b01; + end + if (cyc==2) begin + aaa <= 42'b10; + if (z_0 != 42'h4) $stop; + if (z_1 != 42'h3) $stop; + end + if (cyc==3) begin + if (z_0 != 42'h5) $stop; + if (z_1 != 42'h4) $stop; + end + if (cyc==4) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +module wide ( + input [41:0] xxx, + input [1:0] yyy, + output [41:0] zzz + ); + // verilator public_module + + assign zzz = xxx+ { {40{1'b0}},yyy }; + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_math_cmp.pl b/test_regress/t/t_math_cmp.pl new file mode 100755 index 000000000..7e6b144bc --- /dev/null +++ b/test_regress/t/t_math_cmp.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Revision: 1.1 $$Date$$Author$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_cmp.v b/test_regress/t/t_math_cmp.v new file mode 100644 index 000000000..9af808510 --- /dev/null +++ b/test_regress/t/t_math_cmp.v @@ -0,0 +1,165 @@ +// $Revision: 1.1 $$Date$$Author$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [2:0] index_a; + reg [2:0] index_b; + + prover #(4) p4 (/*AUTOINST*/ + // Inputs + .clk (clk), + .index_a (index_a), + .index_b (index_b)); + prover #(32) p32 (/*AUTOINST*/ + // Inputs + .clk (clk), + .index_a (index_a), + .index_b (index_b)); + prover #(63) p63 (/*AUTOINST*/ + // Inputs + .clk (clk), + .index_a (index_a), + .index_b (index_b)); + prover #(64) p64 (/*AUTOINST*/ + // Inputs + .clk (clk), + .index_a (index_a), + .index_b (index_b)); + prover #(72) p72 (/*AUTOINST*/ + // Inputs + .clk (clk), + .index_a (index_a), + .index_b (index_b)); + prover #(126) p126 (/*AUTOINST*/ + // Inputs + .clk (clk), + .index_a (index_a), + .index_b (index_b)); + prover #(128) p128 (/*AUTOINST*/ + // Inputs + .clk (clk), + .index_a (index_a), + .index_b (index_b)); + + integer cyc; initial cyc=0; + initial index_a = 3'b0; + initial index_b = 3'b0; + always @* begin + index_a = cyc[2:0]; if (index_a>3'd4) index_a=3'd4; + index_b = cyc[5:3]; if (index_b>3'd4) index_b=3'd4; + end + + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc==99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + + +module prover ( + input clk, + input [2:0] index_a, + input [2:0] index_b + ); + + parameter WIDTH = 4; + + + reg signed [WIDTH-1:0] as; + reg signed [WIDTH-1:0] bs; + wire [WIDTH-1:0] b = bs; + + always @* begin + casez (index_a) + 3'd0: as = {(WIDTH){1'd0}}; // 0 + 3'd1: as = {{(WIDTH-1){1'd0}}, 1'b1}; // 1 + 3'd2: as = {1'b0, {(WIDTH-1){1'd0}}}; // 127 or equiv + 3'd3: as = {(WIDTH){1'd1}}; // -1 + 3'd4: as = {1'b1, {(WIDTH-1){1'd0}}}; // -128 or equiv + default: $stop; + endcase + casez (index_b) + 3'd0: bs = {(WIDTH){1'd0}}; // 0 + 3'd1: bs = {{(WIDTH-1){1'd0}}, 1'b1}; // 1 + 3'd2: bs = {1'b0, {(WIDTH-1){1'd0}}}; // 127 or equiv + 3'd3: bs = {(WIDTH){1'd1}}; // -1 + 3'd4: bs = {1'b1, {(WIDTH-1){1'd0}}}; // -128 or equiv + default: $stop; + endcase + end + + reg [7:0] results[4:0][4:0]; + + wire gt = as>b; + wire gts = as>bs; + wire gte = as>=b; + wire gtes = as>=bs; + wire lt = as2) begin + if (0) $write("results[%d][%d] = 8'b%b_%b_%b_%b_%b_%b_%b_%b;\n", + index_a, index_b, + gt, gts, gte, gtes, lt, lts, lte, ltes); + exp = results[index_a][index_b]; + got = {gt, gts, gte, gtes, lt, lts, lte, ltes}; + if (exp !== got) begin + $display("%%Error: bad comparison width=%0d: %d/%d got=%b exp=%b", WIDTH, index_a,index_b,got, exp); + $stop; + end + end + end + + // Result table + initial begin + // Indexes: 0, 1, -1, 127, -128 + // Gt Gts Gte Gtes Lt Lts Lte Ltes + results[0][0] = 8'b0_0_1_1_0_0_1_1; + results[0][1] = 8'b0_0_0_0_1_1_1_1; + results[0][2] = 8'b0_0_1_1_0_0_1_1; + results[0][3] = 8'b0_1_0_1_1_0_1_0; + results[0][4] = 8'b0_1_0_1_1_0_1_0; + results[1][0] = 8'b1_1_1_1_0_0_0_0; + results[1][1] = 8'b0_0_1_1_0_0_1_1; + results[1][2] = 8'b1_1_1_1_0_0_0_0; + results[1][3] = 8'b0_1_0_1_1_0_1_0; + results[1][4] = 8'b0_1_0_1_1_0_1_0; + results[2][0] = 8'b0_0_1_1_0_0_1_1; + results[2][1] = 8'b0_0_0_0_1_1_1_1; + results[2][2] = 8'b0_0_1_1_0_0_1_1; + results[2][3] = 8'b0_1_0_1_1_0_1_0; + results[2][4] = 8'b0_1_0_1_1_0_1_0; + results[3][0] = 8'b1_0_1_0_0_1_0_1; + results[3][1] = 8'b1_0_1_0_0_1_0_1; + results[3][2] = 8'b1_0_1_0_0_1_0_1; + results[3][3] = 8'b0_0_1_1_0_0_1_1; + results[3][4] = 8'b1_1_1_1_0_0_0_0; + results[4][0] = 8'b1_0_1_0_0_1_0_1; + results[4][1] = 8'b1_0_1_0_0_1_0_1; + results[4][2] = 8'b1_0_1_0_0_1_0_1; + results[4][3] = 8'b0_0_0_0_1_1_1_1; + results[4][4] = 8'b0_0_1_1_0_0_1_1; + end + +endmodule diff --git a/test_regress/t/t_math_concat.pl b/test_regress/t/t_math_concat.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_math_concat.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_concat.v b/test_regress/t/t_math_concat.v new file mode 100644 index 000000000..4f92dbdc9 --- /dev/null +++ b/test_regress/t/t_math_concat.v @@ -0,0 +1,73 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [255:0] i; + wire [255:0] q; + + assign q = { + i[176],i[168],i[126],i[177],i[097],i[123],i[231],i[039], + i[156],i[026],i[001],i[052],i[005],i[240],i[157],i[048], + i[111],i[088],i[133],i[225],i[046],i[038],i[004],i[234], + i[115],i[008],i[069],i[099],i[137],i[130],i[255],i[122], + i[223],i[195],i[224],i[083],i[094],i[018],i[067],i[034], + i[221],i[105],i[104],i[107],i[053],i[066],i[020],i[174], + i[010],i[196],i[003],i[041],i[071],i[194],i[154],i[110], + i[186],i[210],i[040],i[044],i[243],i[236],i[239],i[183], + i[164],i[064],i[086],i[193],i[055],i[206],i[203],i[128], + i[190],i[233],i[023],i[022],i[135],i[108],i[061],i[139], + i[180],i[043],i[109],i[090],i[229],i[238],i[095],i[173], + i[208],i[054],i[025],i[024],i[148],i[079],i[246],i[142], + i[181],i[129],i[120],i[220],i[036],i[159],i[201],i[119], + i[216],i[152],i[175],i[138],i[242],i[143],i[101],i[035], + i[228],i[082],i[211],i[062],i[076],i[124],i[150],i[149], + i[235],i[227],i[250],i[134],i[068],i[032],i[060],i[144], + i[042],i[163],i[087],i[059],i[213],i[251],i[200],i[070], + i[145],i[204],i[249],i[191],i[127],i[247],i[106],i[017], + i[028],i[045],i[215],i[162],i[205],i[073],i[065],i[084], + i[153],i[158],i[085],i[197],i[212],i[114],i[096],i[118], + i[146],i[030],i[058],i[230],i[141],i[000],i[199],i[171], + i[182],i[185],i[021],i[016],i[033],i[237],i[015],i[112], + i[222],i[253],i[244],i[031],i[248],i[092],i[226],i[179], + i[189],i[056],i[132],i[116],i[072],i[184],i[027],i[002], + i[103],i[125],i[009],i[078],i[178],i[245],i[170],i[161], + i[102],i[047],i[192],i[012],i[057],i[207],i[187],i[151], + i[218],i[254],i[214],i[037],i[131],i[165],i[011],i[098], + i[169],i[209],i[167],i[202],i[100],i[172],i[147],i[013], + i[136],i[166],i[252],i[077],i[051],i[074],i[140],i[050], + i[217],i[198],i[081],i[091],i[075],i[121],i[188],i[219], + i[160],i[241],i[080],i[155],i[019],i[006],i[014],i[029], + i[089],i[049],i[113],i[232],i[007],i[117],i[063],i[093] + }; + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + $write("%x %x\n", q, i); + if (cyc==1) begin + i <= 256'hed388e646c843d35de489bab2413d77045e0eb7642b148537491f3da147e7f26; + end + if (cyc==2) begin + i <= 256'h0e17c88f3d5fe51a982646c8e2bd68c3e236ddfddddbdad20a48e039c9f395b8; + if (q != 256'h697bad4b0cf2d7fa4ad22809293710bb67d1eb3131e8eb2135f2c7bd820baa84) $stop; + end + if (cyc==3) begin + if (q != 256'h320eda5078b3e942353d16dddc8b29fd773b4fcec8323612dadfb1fa483f602c) $stop; + end + if (cyc==4) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_math_concat64.pl b/test_regress/t/t_math_concat64.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_math_concat64.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_concat64.v b/test_regress/t/t_math_concat64.v new file mode 100644 index 000000000..af40d2705 --- /dev/null +++ b/test_regress/t/t_math_concat64.v @@ -0,0 +1,129 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [127:0] i; + wire [127:0] q1; + wire [127:0] q32; + wire [127:0] q64; + wire [63:0] q64_low; + + assign q1 = { + i[24*4], i[25*4], i[26*4], i[27*4], i[28*4], i[29*4], i[30*4], i[31*4], + i[16*4], i[17*4], i[18*4], i[19*4], i[20*4], i[21*4], i[22*4], i[23*4], + i[8*4], i[9*4], i[10*4], i[11*4], i[12*4], i[13*4], i[14*4], i[15*4], + i[0*4], i[1*4], i[2*4], i[3*4], i[4*4], i[5*4], i[6*4], i[7*4], + + i[24*4+1], i[25*4+1], i[26*4+1], i[27*4+1], i[28*4+1], i[29*4+1], i[30*4+1], i[31*4+1], + i[16*4+1], i[17*4+1], i[18*4+1], i[19*4+1], i[20*4+1], i[21*4+1], i[22*4+1], i[23*4+1], + i[8*4+1], i[9*4+1], i[10*4+1], i[11*4+1], i[12*4+1], i[13*4+1], i[14*4+1], i[15*4+1], + i[0*4+1], i[1*4+1], i[2*4+1], i[3*4+1], i[4*4+1], i[5*4+1], i[6*4+1], i[7*4+1], + + i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], + i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], + i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], + i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2], + + i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], + i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], + i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], + i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; + + assign q64[127:64] = { + i[24*4], i[25*4], i[26*4], i[27*4], i[28*4], i[29*4], i[30*4], i[31*4], + i[16*4], i[17*4], i[18*4], i[19*4], i[20*4], i[21*4], i[22*4], i[23*4], + i[8*4], i[9*4], i[10*4], i[11*4], i[12*4], i[13*4], i[14*4], i[15*4], + i[0*4], i[1*4], i[2*4], i[3*4], i[4*4], i[5*4], i[6*4], i[7*4], + + i[24*4+1], i[25*4+1], i[26*4+1], i[27*4+1], i[28*4+1], i[29*4+1], i[30*4+1], i[31*4+1], + i[16*4+1], i[17*4+1], i[18*4+1], i[19*4+1], i[20*4+1], i[21*4+1], i[22*4+1], i[23*4+1], + i[8*4+1], i[9*4+1], i[10*4+1], i[11*4+1], i[12*4+1], i[13*4+1], i[14*4+1], i[15*4+1], + i[0*4+1], i[1*4+1], i[2*4+1], i[3*4+1], i[4*4+1], i[5*4+1], i[6*4+1], i[7*4+1]}; + assign q64[63:0] = { + i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], + i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], + i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], + i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2], + + i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], + i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], + i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], + i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; + + assign q64_low = { + i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], + i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], + i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], + i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2], + + i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], + i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], + i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], + i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; + + assign q32[127:96] = { + i[24*4], i[25*4], i[26*4], i[27*4], i[28*4], i[29*4], i[30*4], i[31*4], + i[16*4], i[17*4], i[18*4], i[19*4], i[20*4], i[21*4], i[22*4], i[23*4], + i[8*4], i[9*4], i[10*4], i[11*4], i[12*4], i[13*4], i[14*4], i[15*4], + i[0*4], i[1*4], i[2*4], i[3*4], i[4*4], i[5*4], i[6*4], i[7*4]}; + assign q32[95:64] = { + i[24*4+1], i[25*4+1], i[26*4+1], i[27*4+1], i[28*4+1], i[29*4+1], i[30*4+1], i[31*4+1], + i[16*4+1], i[17*4+1], i[18*4+1], i[19*4+1], i[20*4+1], i[21*4+1], i[22*4+1], i[23*4+1], + i[8*4+1], i[9*4+1], i[10*4+1], i[11*4+1], i[12*4+1], i[13*4+1], i[14*4+1], i[15*4+1], + i[0*4+1], i[1*4+1], i[2*4+1], i[3*4+1], i[4*4+1], i[5*4+1], i[6*4+1], i[7*4+1]}; + assign q32[63:32] = { + i[24*4+2], i[25*4+2], i[26*4+2], i[27*4+2], i[28*4+2], i[29*4+2], i[30*4+2], i[31*4+2], + i[16*4+2], i[17*4+2], i[18*4+2], i[19*4+2], i[20*4+2], i[21*4+2], i[22*4+2], i[23*4+2], + i[8*4+2], i[9*4+2], i[10*4+2], i[11*4+2], i[12*4+2], i[13*4+2], i[14*4+2], i[15*4+2], + i[0*4+2], i[1*4+2], i[2*4+2], i[3*4+2], i[4*4+2], i[5*4+2], i[6*4+2], i[7*4+2]}; + assign q32[31:0] = { + i[24*4+3], i[25*4+3], i[26*4+3], i[27*4+3], i[28*4+3], i[29*4+3], i[30*4+3], i[31*4+3], + i[16*4+3], i[17*4+3], i[18*4+3], i[19*4+3], i[20*4+3], i[21*4+3], i[22*4+3], i[23*4+3], + i[8*4+3], i[9*4+3], i[10*4+3], i[11*4+3], i[12*4+3], i[13*4+3], i[14*4+3], i[15*4+3], + i[0*4+3], i[1*4+3], i[2*4+3], i[3*4+3], i[4*4+3], i[5*4+3], i[6*4+3], i[7*4+3]}; + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + $write("%x %x\n", q1, i); + if (cyc==1) begin + i <= 128'hed388e646c843d35de489bab2413d770; + end + if (cyc==2) begin + i <= 128'h0e17c88f3d5fe51a982646c8e2bd68c3; + if (q1 != 128'h06f0b17c6551e269e3ab07723b26fb10) $stop; + if (q1 != q32) $stop; + if (q1 != q64) $stop; + if (q1[63:0] != q64_low) $stop; + end + if (cyc==3) begin + i <= 128'he236ddfddddbdad20a48e039c9f395b8; + if (q1 != 128'h8c6f018c8a992c979a3e7859f29ac36d) $stop; + if (q1 != q32) $stop; + if (q1 != q64) $stop; + if (q1[63:0] != q64_low) $stop; + end + if (cyc==4) begin + i <= 128'h45e0eb7642b148537491f3da147e7f26; + if (q1 != 128'hf45fc07e4fa8524cf9571425f17f9ad7) $stop; + if (q1 != q32) $stop; + if (q1 != q64) $stop; + if (q1[63:0] != q64_low) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_math_const.pl b/test_regress/t/t_math_const.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_math_const.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_const.v b/test_regress/t/t_math_const.v new file mode 100644 index 000000000..7e5f0cac1 --- /dev/null +++ b/test_regress/t/t_math_const.v @@ -0,0 +1,109 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [39:0] con1,con2, con3; + reg [31:0] w32; + + // surefire lint_off UDDSCN + reg [200:0] conw3, conw4; + // surefire lint_on UDDSCN + + reg [16*8-1:0] con__ascii; + + reg [31:0] win; + // Test casting is proper on narrow->wide->narrow conversions + // verilator lint_off WIDTH + wire [49:0] wider = ({18'h0, win} | (1'b1<<32)) - 50'h111; + wire [31:0] wider2 = ({win} | (1'b1<<32)) - 50'd111; + // verilator lint_on WIDTH + wire [31:0] narrow = wider[31:0]; + wire [31:0] narrow2 = wider2[31:0]; + + // surefire lint_off ASWEMB + // surefire lint_off ASWCMB + // surefire lint_off CWECBB + // surefire lint_off CWECSB + + // surefire lint_off STMINI + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + $write("[%0t] t_const: Running\n",$time); + + con1 = 4_0'h1000_0010; // Odd but legal _ in width + con2 = 40'h10_0000_0010; + con3 = con1 + 40'h10_1100_0101; + if (con1[31:0]!== 32'h1000_0010 || con1[39:32]!==0) $stop; + $display("%x %x %x\n", con2, con2[31:0], con2[39:32]); + if (con2[31:0]!== 32'h10 || con2[39:32]!==8'h10) $stop; + if (con3[31:0]!==32'h2100_0111 || con3[39:32]!==8'h10) $stop; + + // verilator lint_off WIDTH + con1 = 10'h10 + 40'h80_1100_0131; + // verilator lint_on WIDTH + con2 = 40'h80_0000_0000 + 40'h13_7543_0107; + if (con1[31:0]!== 32'h1100_0141 || con1[39:32]!==8'h80) $stop; + if (con2[31:0]!== 32'h7543_0107 || con2[39:32]!==8'h93) $stop; + + // verilator lint_off WIDTH + conw3 = 94'h000a_5010_4020_3030_2040_1050; + // verilator lint_on WIDTH + if (conw3[31:00]!== 32'h2040_1050 || + conw3[63:32]!== 32'h4020_3030 || + conw3[95:64]!== 32'h000a_5010 || + conw3[128:96]!==33'h0) $stop; + $display("%x... %x\n", conw3[15:0], ~| conw3[15:0]); + if ((~| conw3[15:0]) !== 1'h0) $stop; + if ((~& conw3[15:0]) !== 1'h1) $stop; + + // verilator lint_off WIDTH + conw4 = 112'h7010_602a_5030_4040_3050_2060_1070; + // verilator lint_on WIDTH + if (conw4[31:00]!== 32'h2060_1070 || + conw4[63:32]!== 32'h4040_3050 || + conw4[95:64]!== 32'h602a_5030 || + conw4[127:96]!==32'h7010) $stop; + // conw4 = 144'h7000_7000_7010_602a_5030_4040_3050_2060_1070; + + w32 = 12; + win <= 12; + if ((32'hffff0000 >> w32) != 32'h 000ffff0) $stop; + + con__ascii = "abcdefghijklmnop"; + if ( con__ascii !== {"abcd","efgh","ijkl","mnop"}) $stop; + + if ( 3'dx !== 3'hx) $stop; + end + if (cyc==2) begin + win <= 32'h123123; + if (narrow !== 32'hfffffefb) $stop; + if (narrow2 !== 32'hffffff9d) $stop; + end + if (cyc==3) begin + if (narrow !== 32'h00123012) $stop; + if (narrow2 !== 32'h001230b4) $stop; + end + if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_math_div.pl b/test_regress/t/t_math_div.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_math_div.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_div.v b/test_regress/t/t_math_div.v new file mode 100644 index 000000000..e5988b60f --- /dev/null +++ b/test_regress/t/t_math_div.v @@ -0,0 +1,96 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [255:0] a; + reg [60:0] divisor; + reg [60:0] qq; + reg [60:0] rq; + reg signed [60:0] qqs; + reg signed [60:0] rqs; + + always @* begin + qq = a[60:0] / divisor; + rq = a[60:0] % divisor; + qqs = $signed(a[60:0]) / $signed(divisor); + rqs = $signed(a[60:0]) % $signed(divisor); + end + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + //$write("%d: %x %x %x %x\n", cyc, qq, rq, qqs, rqs); + if (cyc==1) begin + a <= 256'hed388e646c843d35de489bab2413d77045e0eb7642b148537491f3da147e7f26; + divisor <= 61'h12371; + a[60] <= 1'b0; divisor[60] <= 1'b0; // Unsigned + end + if (cyc==2) begin + a <= 256'h0e17c88f3d5fe51a982646c8e2bd68c3e236ddfddddbdad20a48e039c9f395b8; + divisor <= 61'h1238123771; + a[60] <= 1'b0; divisor[60] <= 1'b0; // Unsigned +//$display("FIX"); + if (qq!==61'h00000403ad81c0da) $stop; + if (rq!==61'h00000000000090ec) $stop; + if (qqs!==61'h00000403ad81c0da) $stop; + if (rqs!==61'h00000000000090ec) $stop; + end + if (cyc==3) begin + a <= 256'h0e17c88f00d5fe51a982646c8002bd68c3e236ddfd00ddbdad20a48e00f395b8; + divisor <= 61'hf1b; + a[60] <= 1'b1; divisor[60] <= 1'b0; // Signed + if (qq!==61'h000000000090832e) $stop; + if (rq!==61'h0000000334becc6a) $stop; + if (qqs!==61'h000000000090832e) $stop; + if (rqs!==61'h0000000334becc6a) $stop; + end + if (cyc==4) begin + a[60] <= 1'b0; divisor[60] <= 1'b1; // Signed + if (qq!==61'h0001eda37cca1be8) $stop; + if (rq!==61'h0000000000000c40) $stop; + if (qqs!==61'h1fffcf5187c76510) $stop; + if (rqs!==61'h1ffffffffffffd08) $stop; + end + if (cyc==5) begin + a[60] <= 1'b1; divisor[60] <= 1'b1; // Signed + if (qq!==61'h0000000000000000) $stop; + if (rq!==61'h0d20a48e00f395b8) $stop; + if (qqs!==61'h0000000000000000) $stop; + if (rqs!==61'h0d20a48e00f395b8) $stop; + end + if (cyc==6) begin + if (qq!==61'h0000000000000001) $stop; + if (rq!==61'h0d20a48e00f3869d) $stop; + if (qqs!==61'h0000000000000000) $stop; + if (rqs!==61'h1d20a48e00f395b8) $stop; + end + // Div by zero + if (cyc==9) begin + divisor <= 61'd0; + end + if (cyc==10) begin +`ifdef verilator + if (qq !== {61{1'b0}}) $stop; + if (rq !== {61{1'b0}}) $stop; +`else + if (qq !== {61{1'bx}}) $stop; + if (rq !== {61{1'bx}}) $stop; +`endif + end + if (cyc==19) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_math_imm.pl b/test_regress/t/t_math_imm.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_math_imm.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_imm.v b/test_regress/t/t_math_imm.v new file mode 100644 index 000000000..5c9cdabc3 --- /dev/null +++ b/test_regress/t/t_math_imm.v @@ -0,0 +1,113 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. +// +// Example module to create problem. +// +// generate a 64 bit value with bits +// [HighMaskSel_Bot : LowMaskSel_Bot ] = 1 +// [HighMaskSel_Top+32: LowMaskSel_Top+32] = 1 +// all other bits zero. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=0; + reg [7:0] crc; + reg [63:0] sum; + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [63:0] HighLogicImm; // From example of example.v + wire [63:0] LogicImm; // From example of example.v + wire [63:0] LowLogicImm; // From example of example.v + // End of automatics + + wire [5:0] LowMaskSel_Top = crc[5:0]; + wire [5:0] LowMaskSel_Bot = crc[5:0]; + wire [5:0] HighMaskSel_Top = crc[5:0]+{4'b0,crc[7:6]}; + wire [5:0] HighMaskSel_Bot = crc[5:0]+{4'b0,crc[7:6]}; + + example example (/*AUTOINST*/ + // Outputs + .LogicImm (LogicImm[63:0]), + .LowLogicImm (LowLogicImm[63:0]), + .HighLogicImm (HighLogicImm[63:0]), + // Inputs + .LowMaskSel_Top (LowMaskSel_Top[5:0]), + .HighMaskSel_Top (HighMaskSel_Top[5:0]), + .LowMaskSel_Bot (LowMaskSel_Bot[5:0]), + .HighMaskSel_Bot (HighMaskSel_Bot[5:0])); + + always @ (posedge clk) begin + cyc <= cyc + 1; + crc <= {crc[6:0], ~^ {crc[7],crc[5],crc[4],crc[3]}}; + if (0) $write("[%0t] cyc==%0d crc=%b %d.%d,%d.%d -> %x.%x -> %x\n",$time, cyc, crc, + LowMaskSel_Top, HighMaskSel_Top, LowMaskSel_Bot, HighMaskSel_Bot, + LowLogicImm, HighLogicImm, LogicImm); + if (cyc==0) begin + // Single case + crc <= 8'h0; + sum <= 64'h0; + end + else if (cyc==1) begin + // Setup + crc <= 8'hed; + sum <= 64'h0; + end + else if (cyc<90) begin + sum <= {sum[62:0],sum[63]} ^ LogicImm; + end + else if (cyc==99) begin + $write("[%0t] cyc==%0d crc=%b %x\n",$time, cyc, crc, sum); + if (crc != 8'b00111000) $stop; + if (sum != 64'h58743ffa61e41075) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module example (/*AUTOARG*/ + // Outputs + LogicImm, LowLogicImm, HighLogicImm, + // Inputs + LowMaskSel_Top, HighMaskSel_Top, LowMaskSel_Bot, HighMaskSel_Bot + ); + + input [5:0] LowMaskSel_Top, HighMaskSel_Top; + input [5:0] LowMaskSel_Bot, HighMaskSel_Bot; + output [63:0] LogicImm; + + output [63:0] LowLogicImm, HighLogicImm; //FIX + + + wire [63:0] LowLogicImm, HighLogicImm; + + /* verilator lint_off UNSIGNED */ + /* verilator lint_off CMPCONST */ + genvar i; + generate + for (i=0;i<64;i=i+1) begin : MaskVal + if (i >= 32) begin + assign LowLogicImm[i] = (LowMaskSel_Top <= i[5:0]); + assign HighLogicImm[i] = (HighMaskSel_Top >= i[5:0]); + end + else begin + assign LowLogicImm[i] = (LowMaskSel_Bot <= i[5:0]); + assign HighLogicImm[i] = (HighMaskSel_Bot >= i[5:0]); + end + end + endgenerate + /* verilator lint_on UNSIGNED */ + /* verilator lint_on CMPCONST */ + + assign LogicImm = LowLogicImm & HighLogicImm; +endmodule + diff --git a/test_regress/t/t_math_imm2.cpp b/test_regress/t/t_math_imm2.cpp new file mode 100644 index 000000000..cc9e7c910 --- /dev/null +++ b/test_regress/t/t_math_imm2.cpp @@ -0,0 +1,50 @@ +#include +#include "Vt_math_imm2.h" + +QData MaskVal (int lbit, int hbit) { + QData val; + for (val = 0; lbit <= hbit; lbit++) + val |= (1ULL << lbit); + return val; +} + +int main (int argc, char *argv[]) { + Verilated::debug(0); + + Vt_math_imm2 *sim = new Vt_math_imm2; + int lbit, hbit; + + int errs=0; + for (lbit = 0; lbit < 32; lbit++) { + for (hbit = lbit; hbit < 32; hbit++) { + QData expected; + + sim->LowMaskSel_Bot = lbit; + sim->LowMaskSel_Top = lbit; + sim->HighMaskSel_Bot = hbit; + sim->HighMaskSel_Top = hbit; + + sim->eval(); + + expected = (MaskVal (sim->LowMaskSel_Top, sim->HighMaskSel_Top) << 32ULL) + | MaskVal (sim->LowMaskSel_Bot, sim->HighMaskSel_Bot); + + if (sim->LogicImm != expected) { + printf ("%%Error: %d.%d,%d.%d -> %016llx/%016llx -> %016llx (expected %016llx)\n", + sim->LowMaskSel_Top, sim->HighMaskSel_Top, + sim->LowMaskSel_Bot, sim->HighMaskSel_Bot, + sim->LowLogicImm, sim->HighLogicImm, + sim->LogicImm, expected); + errs=1; + } + } + } + + if (errs) { + vl_stop(__FILE__, __LINE__, "TOP-cpp"); + exit(10); + } else { + printf ("*-* All Finished *-*\n"); + exit(0); + } +} diff --git a/test_regress/t/t_math_imm2.pl b/test_regress/t/t_math_imm2.pl new file mode 100755 index 000000000..7ffe31944 --- /dev/null +++ b/test_regress/t/t_math_imm2.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + make_top_shell => 0, + make_main => 0, + v_flags2 => ["--exe t/$Last_Self->{name}.cpp"], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_imm2.v b/test_regress/t/t_math_imm2.v new file mode 100644 index 000000000..02af75dfa --- /dev/null +++ b/test_regress/t/t_math_imm2.v @@ -0,0 +1,44 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. +// +// Example module to create problem. +// +// generate a 64 bit value with bits +// [HighMaskSel_Bot : LowMaskSel_Bot ] = 1 +// [HighMaskSel_Top+32: LowMaskSel_Top+32] = 1 +// all other bits zero. + +module t_math_imm2 (/*AUTOARG*/ + // Outputs + LogicImm, LowLogicImm, HighLogicImm, + // Inputs + LowMaskSel_Top, HighMaskSel_Top, LowMaskSel_Bot, HighMaskSel_Bot + ); + input [4:0] LowMaskSel_Top, HighMaskSel_Top; + input [4:0] LowMaskSel_Bot, HighMaskSel_Bot; + output [63:0] LogicImm; + + output [63:0] LowLogicImm, HighLogicImm; + + /* verilator lint_off UNSIGNED */ + /* verilator lint_off CMPCONST */ + genvar i; + generate + for (i=0;i<64;i=i+1) begin : MaskVal + if (i >= 32) begin + assign LowLogicImm[i] = (LowMaskSel_Top <= i[4:0]); + assign HighLogicImm[i] = (HighMaskSel_Top >= i[4:0]); + end + else begin + assign LowLogicImm[i] = (LowMaskSel_Bot <= i[4:0]); + assign HighLogicImm[i] = (HighMaskSel_Bot >= i[4:0]); + end + end + endgenerate + + assign LogicImm = LowLogicImm & HighLogicImm; +endmodule + diff --git a/test_regress/t/t_math_pow.pl b/test_regress/t/t_math_pow.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_math_pow.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_pow.v b/test_regress/t/t_math_pow.v new file mode 100644 index 000000000..6834f2e31 --- /dev/null +++ b/test_regress/t/t_math_pow.v @@ -0,0 +1,95 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [60:0] p; + reg [60:0] a; + reg [20:0] b; + reg [60:0] shifted; + + always @* begin + p = a[60:0] ** b[20:0]; + shifted = 2 ** b[20:0]; + end + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + $write("%0x %x %x\n", cyc, p, shifted); + // Constant versions + if (61'h1 ** 21'h31 != 61'h1) $stop; + if (61'h2 ** 21'h10 != 61'h10000) $stop; + if (61'd10 ** 21'h3 != 61'h3e8) $stop; + if (61'h3 ** 21'h7 != 61'h88b) $stop; + if (61'h7ab3811219 ** 21'ha6e30 != 61'h01ea58c703687e81) $stop; + if (cyc==1) begin + a <= 61'h0; + b <= 21'h0; + end + if (cyc==2) begin + a <= 61'h0; + b <= 21'h3; + end + if (cyc==3) begin + a <= 61'h1; + b <= 21'h31; + end + if (cyc==4) begin + a <= 61'h2; + b <= 21'h10; + end + if (cyc==5) begin + a <= 61'd10; + b <= 21'd3; + end + if (cyc==6) begin + a <= 61'd3; + b <= 21'd7; + end + if (cyc==7) begin + a <= 61'h7ab3811219; + b <= 21'ha6e30; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + case (cyc) + 32'd00: ; + 32'd01: ; + 32'd02: ; // 0^x is indeterminate + 32'd03: ; // 0^x is indeterminate + 32'd04: if (p!=61'h1) $stop; + 32'd05: if (p!=61'h10000) $stop; + 32'd06: if (p!=61'h3e8) $stop; + 32'd07: if (p!=61'h88b) $stop; + 32'd08: if (p!=61'h01ea58c703687e81) $stop; + 32'd09: if (p!=61'h01ea58c703687e81) $stop; + default: $stop; + endcase + case (cyc) + 32'd00: ; + 32'd01: ; + 32'd02: if (shifted!=61'h0000000000000001) $stop; + 32'd03: if (shifted!=61'h0000000000000008) $stop; + 32'd04: if (shifted!=61'h0002000000000000) $stop; + 32'd05: if (shifted!=61'h0000000000010000) $stop; + 32'd06: if (shifted!=61'h0000000000000008) $stop; + 32'd07: if (shifted!=61'h0000000000000080) $stop; + 32'd08: if (shifted!=61'h0000000000000000) $stop; + 32'd09: if (shifted!=61'h0000000000000000) $stop; + default: $stop; + endcase + end +endmodule diff --git a/test_regress/t/t_math_repl.pl b/test_regress/t/t_math_repl.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_math_repl.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_repl.v b/test_regress/t/t_math_repl.v new file mode 100644 index 000000000..df4374707 --- /dev/null +++ b/test_regress/t/t_math_repl.v @@ -0,0 +1,102 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [63:0] rf; + reg [63:0] rf2; + reg [63:0] biu; + reg okidoki; + + always @* begin + rf[63:32] = biu[63:32] & {32{okidoki}}; + rf[31:0] = {32{okidoki}}; + rf2 = rf; + rf2[31:0] = ~{32{okidoki}}; + end + + reg [31:0] src1, src0, sr, mask; + wire [31:0] dualasr + = ((| src1[31:4]) + ? {{16{src0[31]}}, {16{src0[15]}}} + : ( ( sr & {2{mask[31:16]}}) + | ( {{16{src0[31]}}, {16{src0[15]}}} + & {2{~mask[31:16]}}))); + + wire [31:0] sl_mask + = (32'hffffffff << src1[4:0]); + + wire [31:0] sr_mask + = {sl_mask[0], sl_mask[1], + sl_mask[2], sl_mask[3], sl_mask[4], + sl_mask[5], sl_mask[6], sl_mask[7], + sl_mask[8], sl_mask[9], + sl_mask[10], sl_mask[11], + sl_mask[12], sl_mask[13], sl_mask[14], + sl_mask[15], sl_mask[16], + sl_mask[17], sl_mask[18], sl_mask[19], + sl_mask[20], sl_mask[21], + sl_mask[22], sl_mask[23], sl_mask[24], + sl_mask[25], sl_mask[26], + sl_mask[27], sl_mask[28], sl_mask[29], + sl_mask[30], sl_mask[31]}; + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + $write("%x %x %x %x %x\n", rf, rf2, dualasr, sl_mask, sr_mask); + if (cyc==1) begin + biu <= 64'h12451282_abadee00; + okidoki <= 1'b0; + src1 <= 32'h00000001; + src0 <= 32'h9a4f1235; + sr <= 32'h0f19f567; + mask <= 32'h7af07ab4; + end + if (cyc==2) begin + biu <= 64'h12453382_abad8801; + okidoki <= 1'b1; + if (rf != 64'h0) $stop; + if (rf2 != 64'h00000000ffffffff) $stop; + src1 <= 32'h0010000f; + src0 <= 32'h028aa336; + sr <= 32'h42ad0377; + mask <= 32'h1ab3b906; + if (dualasr != 32'h8f1f7060) $stop; + if (sl_mask != 32'hfffffffe) $stop; + if (sr_mask != 32'h7fffffff) $stop; + end + if (cyc==3) begin + biu <= 64'h12422382_77ad8802; + okidoki <= 1'b1; + if (rf != 64'h12453382ffffffff) $stop; + if (rf2 != 64'h1245338200000000) $stop; + src1 <= 32'h0000000f; + src0 <= 32'h5c158f71; + sr <= 32'h7076c40a; + mask <= 32'h33eb3d44; + if (dualasr != 32'h0000ffff) $stop; + if (sl_mask != 32'hffff8000) $stop; + if (sr_mask != 32'h0001ffff) $stop; + end + if (cyc==4) begin + if (rf != 64'h12422382ffffffff) $stop; + if (rf2 != 64'h1242238200000000) $stop; + if (dualasr != 32'h3062cc1e) $stop; + if (sl_mask != 32'hffff8000) $stop; + if (sr_mask != 32'h0001ffff) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_math_reverse.pl b/test_regress/t/t_math_reverse.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_math_reverse.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_reverse.v b/test_regress/t/t_math_reverse.v new file mode 100644 index 000000000..e3b2681be --- /dev/null +++ b/test_regress/t/t_math_reverse.v @@ -0,0 +1,87 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [7:0] crc; + + // Build up assignments + wire [7:0] bitrev; + assign bitrev[7] = crc[0]; + assign bitrev[6] = crc[1]; + assign bitrev[5] = crc[2]; + assign bitrev[4] = crc[3]; + assign bitrev[0] = crc[7]; + assign bitrev[1] = crc[6]; + assign bitrev[2] = crc[5]; + assign bitrev[3] = crc[4]; + + // Build up always assignments + reg [7:0] bitrevb; + always @ (/*AS*/crc) begin + bitrevb[7] = crc[0]; + bitrevb[6] = crc[1]; + bitrevb[5] = crc[2]; + bitrevb[4] = crc[3]; + bitrevb[0] = crc[7]; + bitrevb[1] = crc[6]; + bitrevb[2] = crc[5]; + bitrevb[3] = crc[4]; + end + + // Build up always assignments + reg [7:0] bitrevr; + always @ (posedge clk) begin + bitrevr[7] <= crc[0]; + bitrevr[6] <= crc[1]; + bitrevr[5] <= crc[2]; + bitrevr[4] <= crc[3]; + bitrevr[0] <= crc[7]; + bitrevr[1] <= crc[6]; + bitrevr[2] <= crc[5]; + bitrevr[3] <= crc[4]; + end + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc<=cyc+1; + //$write("cyc=%0d crc=%x r=%x\n", cyc, crc, bitrev); + crc <= {crc[6:0], ~^ {crc[7],crc[5],crc[4],crc[3]}}; + if (cyc==1) begin + crc <= 8'hed; + end + if (cyc==2 && bitrev!=8'hb7) $stop; + if (cyc==3 && bitrev!=8'h5b) $stop; + if (cyc==4 && bitrev!=8'h2d) $stop; + if (cyc==5 && bitrev!=8'h16) $stop; + if (cyc==6 && bitrev!=8'h8b) $stop; + if (cyc==7 && bitrev!=8'hc5) $stop; + if (cyc==8 && bitrev!=8'he2) $stop; + if (cyc==9 && bitrev!=8'hf1) $stop; + if (bitrevb != bitrev) $stop; + if (cyc==3 && bitrevr!=8'hb7) $stop; + if (cyc==4 && bitrevr!=8'h5b) $stop; + if (cyc==5 && bitrevr!=8'h2d) $stop; + if (cyc==6 && bitrevr!=8'h16) $stop; + if (cyc==7 && bitrevr!=8'h8b) $stop; + if (cyc==8 && bitrevr!=8'hc5) $stop; + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_math_shift.pl b/test_regress/t/t_math_shift.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_math_shift.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_shift.v b/test_regress/t/t_math_shift.v new file mode 100644 index 000000000..a51e67cdb --- /dev/null +++ b/test_regress/t/t_math_shift.v @@ -0,0 +1,97 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [31:0] right; + reg [31:0] left; + reg [63:0] qright; + reg [63:0] qleft; + reg [31:0] amt; + + always @* begin + right = 32'h819b018a >> amt; + left = 32'h819b018a << amt; + qright = 64'hf784bf8f_12734089 >> amt; + qleft = 64'hf784bf8f_12734089 >> amt; + end + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + $write("%d %x %x %x %x\n", cyc, left, right, qleft, qright); + if (cyc==1) begin + amt <= 32'd0; + if (5'b10110>>2 != 5'b00101) $stop; + if (5'b10110>>>2 != 5'b00101) $stop; // Note it cares about sign-ness + if (5'b10110<<2 != 5'b11000) $stop; + if (5'b10110<<<2 != 5'b11000) $stop; + if (5'sb10110>>2 != 5'sb00101) $stop; + if (5'sb10110>>>2 != 5'sb11101) $stop; + if (5'sb10110<<2 != 5'sb11000) $stop; + if (5'sb10110<<<2 != 5'sb11000) $stop; + end + if (cyc==2) begin + amt <= 32'd28; + if (left != 32'h819b018a) $stop; + if (right != 32'h819b018a) $stop; + if (qleft != 64'hf784bf8f_12734089) $stop; + if (qright != 64'hf784bf8f_12734089) $stop; + end + if (cyc==3) begin + amt <= 32'd31; + if (left != 32'ha0000000) $stop; + if (right != 32'h8) $stop; + if (qleft != 64'h0000000f784bf8f1) $stop; + if (qright != 64'h0000000f784bf8f1) $stop; + end + if (cyc==4) begin + amt <= 32'd32; + if (left != 32'h0) $stop; + if (right != 32'h1) $stop; + if (qleft != 64'h00000001ef097f1e) $stop; + if (qright != 64'h00000001ef097f1e) $stop; + end + if (cyc==5) begin + amt <= 32'd33; + if (left != 32'h0) $stop; + if (right != 32'h0) $stop; + if (qleft != 64'h00000000f784bf8f) $stop; + if (qright != 64'h00000000f784bf8f) $stop; + end + if (cyc==6) begin + amt <= 32'd64; + if (left != 32'h0) $stop; + if (right != 32'h0) $stop; + if (qleft != 64'h000000007bc25fc7) $stop; + if (qright != 64'h000000007bc25fc7) $stop; + end + if (cyc==7) begin + amt <= 32'd128; + if (left != 32'h0) $stop; + if (right != 32'h0) $stop; + if (qleft != 64'h0) $stop; + if (qright != 64'h0) $stop; + end + if (cyc==8) begin + if (left != 32'h0) $stop; + if (right != 32'h0) $stop; + if (qleft != 64'h0) $stop; + if (qright != 64'h0) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_math_shiftrs.pl b/test_regress/t/t_math_shiftrs.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_math_shiftrs.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_shiftrs.v b/test_regress/t/t_math_shiftrs.v new file mode 100644 index 000000000..edc530990 --- /dev/null +++ b/test_regress/t/t_math_shiftrs.v @@ -0,0 +1,57 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg signed [64+15:0] data; + integer i; + integer b; + reg signed [64+15:0] srs; + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==2) begin + data <= 80'h0; + data[75] <= 1'b1; + data[10] <= 1'b1; + end + if (cyc==3) begin + for (i=0; i<85; i=i+1) begin + srs = data>>>i; + //$write (" %x >>> %d == %x\n",data,i,srs); + for (b=0; b<80; b=b+1) begin + if (srs[b] != (b==(75-i) || b==(10-i))) $stop; + end + end + end + if (cyc==10) begin + data <= 80'h0; + data[79] <= 1'b1; + data[10] <= 1'b1; + end + if (cyc==12) begin + for (i=0; i<85; i=i+1) begin + srs = data>>>i; + //$write (" %x >>> %d == %x\n",data,i,srs); + for (b=0; b<80; b=b+1) begin + if (srs[b] != (b>=(79-i) || b==(10-i))) $stop; + end + end + end + if (cyc==20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_math_signed.pl b/test_regress/t/t_math_signed.pl new file mode 100755 index 000000000..ffce37ad9 --- /dev/null +++ b/test_regress/t/t_math_signed.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Revision: 1.1 $$Date$$Author$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_signed.v b/test_regress/t/t_math_signed.v new file mode 100644 index 000000000..228a03aec --- /dev/null +++ b/test_regress/t/t_math_signed.v @@ -0,0 +1,164 @@ +// $Revision: 1.1 $$Date$$Author$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + by_width #(1) w1 (.clk(clk)); + by_width #(31) w31 (.clk(clk)); + by_width #(32) w32 (.clk(clk)); + by_width #(33) w33 (.clk(clk)); + by_width #(63) w63 (.clk(clk)); + by_width #(64) w64 (.clk(clk)); + by_width #(65) w65 (.clk(clk)); + by_width #(95) w95 (.clk(clk)); + by_width #(96) w96 (.clk(clk)); + by_width #(97) w97 (.clk(clk)); + + reg signed [15:0] a; + reg signed [4:0] b; + + reg signed [15:0] sr,srs,sl,sls; + + reg [15:0] b_s; + reg [15:0] b_us; + + always @* begin + sr = a>>b; + srs = copy_signed(a)>>>b; + sl = a<>>4; // Signed + b_us = b[4:0]>>>4; // Unsigned, due to extract + // verilator lint_on WIDTH + end + + integer i; + initial begin + if ((-1 >>> 3) != -1) $stop; // Decimals are signed + if ((3'b111 >>> 3) != 0) $stop; // Based numbers are unsigned + if ((3'sb111 >>> 3) != -1) $stop; // Signed based numbers + if ( (3'sb000 > 3'sb000)) $stop; + if (!(3'sb000 > 3'sb111)) $stop; + if ( (3'sb111 > 3'sb000)) $stop; + if ( (3'sb000 < 3'sb000)) $stop; + if ( (3'sb000 < 3'sb111)) $stop; + if (!(3'sb111 < 3'sb000)) $stop; + if (!(3'sb000 >= 3'sb000)) $stop; + if (!(3'sb000 >= 3'sb111)) $stop; + if ( (3'sb111 >= 3'sb000)) $stop; + if (!(3'sb000 <= 3'sb000)) $stop; + if ( (3'sb000 <= 3'sb111)) $stop; + if (!(3'sb111 <= 3'sb000)) $stop; + // When we multiply overflow, the sign bit stays correct. + if ( (4'sd2*4'sd8) != 4'd0) $stop; + // From the spec: + // verilator lint_off WIDTH + i = -12 /3; if (i !== 32'hfffffffc) $stop; + i = -'d12 /3; if (i !== 32'h55555551) $stop; + i = -'sd12 /3; if (i !== 32'hfffffffc) $stop; + i = -4'sd12 /3; if (i !== 32'h00000001) $stop; + // verilator lint_on WIDTH + end + + function signed [15:0] copy_signed; + input [15:0] ai; + copy_signed = ai; + endfunction + + integer cyc; initial cyc=0; + always @ (posedge clk) begin + cyc <= cyc + 1; + $write("%x %x %x %x %x %x %x\n", cyc, sr,srs,sl,sls, b_s,b_us); + case (cyc) + 0: begin + a <= 16'sh8b1b; b <= 5'sh1f; // -1 + end + 1: begin + // Check spaces in constants + a <= 16 'sh 8b1b; b <= 5'sh01; // -1 + end + 2: begin + a <= 16'sh8b1b; b <= 5'sh1e; // shift AMOUNT is really unsigned + end + 3: begin + a <= 16'sh0048; b <= 5'sh1f; + end + 4: begin + a <= 16'sh4154; b <= 5'sh02; + end + 5: begin + a <= 16'shc3e8; b <= 5'sh12; + end + 6: begin + a <= 16'sh488b; b <= 5'sh02; + end + 9: begin + $write("*-* All Finished *-*\n"); + $finish; + end + default: ; + endcase + case (cyc) + 0: ; + 1: if ({sr,srs,sl,sls,b_s,b_us}!==96'sh0000_ffff_0000_0000_ffff_0001) $stop; + 2: if ({sr,srs,sl,sls,b_s,b_us}!==96'sh458d_c58d_1636_1636_0000_0000) $stop; + 3: if ({sr,srs,sl,sls,b_s,b_us}!==96'sh0000_ffff_0000_0000_ffff_0001) $stop; + 4: if ({sr,srs,sl,sls,b_s,b_us}!==96'sh0000_0000_0000_0000_ffff_0001) $stop; + 5: if ({sr,srs,sl,sls,b_s,b_us}!==96'sh1055_1055_0550_0550_0000_0000) $stop; + 6: if ({sr,srs,sl,sls,b_s,b_us}!==96'sh0000_ffff_0000_0000_ffff_0001) $stop; + 7: if ({sr,srs,sl,sls,b_s,b_us}!==96'sh1222_1222_222c_222c_0000_0000) $stop; + 8: ; + 9: ; + endcase + end +endmodule + + +module by_width ( + input clk + ); + parameter WIDTH=1; + + reg signed i1; + reg signed [62:0] i63; + reg signed [64:0] i65; + + // verilator lint_off WIDTH + wire signed [WIDTH-1:0] i1extp /*verilator public*/ = i1; + wire signed [WIDTH-1:0] i1ext = i1; + wire signed [WIDTH-1:0] i63ext = i63; + wire signed [WIDTH-1:0] i65ext = i65; + // verilator lint_on WIDTH + + integer cyc; initial cyc=0; + always @ (posedge clk) begin + cyc <= cyc + 1; + i1 <= cyc[0]; + i63 <= {63{cyc[0]}}; + i65 <= {65{cyc[0]}}; + case (cyc) + 1: begin + if (i1extp != {WIDTH{1'b0}}) $stop; + if (i1ext != {WIDTH{1'b0}}) $stop; + if (i63ext != {WIDTH{1'b0}}) $stop; + if (i65ext != {WIDTH{1'b0}}) $stop; + end + 2: begin + if (i1extp != {WIDTH{1'b1}}) $stop; + if (i1ext != {WIDTH{1'b1}}) $stop; + if (i63ext != {WIDTH{1'b1}}) $stop; + if (i65ext != {WIDTH{1'b1}}) $stop; + end + default: ; + endcase + end +endmodule diff --git a/test_regress/t/t_math_svl.pl b/test_regress/t/t_math_svl.pl new file mode 100755 index 000000000..ce8d370c2 --- /dev/null +++ b/test_regress/t/t_math_svl.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2005 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. + +compile ( + ) if $Last_Self->{v3}; + +execute ( + check_finished=>1, + # Make sure we get the finish statement called + expect=> +'\*-\* All Finished \*-\* +Goodbye world, at cycle \d+.*', + ) if $Last_Self->{v3}; + +ok(1); +1; diff --git a/test_regress/t/t_math_svl.v b/test_regress/t/t_math_svl.v new file mode 100644 index 000000000..890da8670 --- /dev/null +++ b/test_regress/t/t_math_svl.v @@ -0,0 +1,130 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [15:0] l; + reg [49:0] q; + reg [79:0] w; + reg [4:0] lc; + reg lo; + reg l0; + reg [5:0] qc; + reg qo; + reg q0; + reg [6:0] wc; + reg wo; + reg w0; + + always_comb @* begin + lc = $countones(l); + lo = $onehot(l); + l0 = $onehot0(l); + wc = $countones(w); + wo = $onehot(w); + w0 = $onehot0(w); + qc = $countones(q); + qo = $onehot(q); + q0 = $onehot0(q); + end + + integer cyc; initial cyc=1; + always_ff @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + //$write("%d %x %d %x %x %x %d %x %x %x %d %x %x\n", + // cyc, l, lc, lo, l0, q,qc,qo,q0, w,wc,wo,w0); + if (cyc==0) begin + // Constification check + if ($countones(32'b11001011101) != 7) $stop; + if ($countones(32'b0) != 0) $stop; + if ($isunknown(32'b11101x11111) != 1) $stop; + if ($isunknown(32'b11101011111) != 0) $stop; + if ($isunknown(32'b10zzzzzzzzz) != 0) $stop; + if ($bits(0) != 32'd32) $stop; + if ($bits(lc) != 5) $stop; + if ($onehot(32'b00000001000000) != 1'b1) $stop; + if ($onehot(32'b00001001000000) != 1'b0) $stop; + if ($onehot(32'b0) != 1'b0) $stop; + if ($onehot0(32'b00000001000000) != 1'b1) $stop; + if ($onehot0(32'b00001001000000) != 1'b0) $stop; + if ($onehot0(32'b0) != 1'b1) $stop; + end + if (cyc==1) begin + l <= 16'b0; + q <= 50'h0; + w <= 80'h0; + end + if (cyc==2) begin + l <= ~16'b0; + q <= ~50'h0; + w <= ~80'h0; + // + if ({lc,lo,l0} != {5'd0,1'b0,1'b1}) $stop; + if ({qc,qo,q0} != {6'd0,1'b0,1'b1}) $stop; + if ({wc,wo,w0} != {7'd0,1'b0,1'b1}) $stop; + end + if (cyc==3) begin + l <= 16'b0010110010110111; + q <= 50'h01_1111_0001; + w <= 80'h0100_0000_0f00_00f0_0000; + // + if ({lc,lo,l0} != {5'd16,1'b0,1'b0}) $stop; + if ({qc,qo,q0} != {6'd50,1'b0,1'b0}) $stop; + if ({wc,wo,w0} != {7'd80,1'b0,1'b0}) $stop; + end + if (cyc==4) begin + l <= 16'b0000010000000000; + q <= 50'h1_0000_0000; + w <= 80'h010_00000000_00000000; + // + if ({lc,lo,l0} != {5'd9,1'b0,1'b0}) $stop; + if ({qc,qo,q0} != {6'd6,1'b0,1'b0}) $stop; + if ({wc,wo,w0} != {7'd9,1'b0,1'b0}) $stop; + end + if (cyc==5) begin + l <= 16'b0000000100000000; + q <= 50'h8000_0000_0000; + w <= 80'h10_00000000_00000000; + // + if ({lc,lo,l0} != {5'd1,1'b1,1'b1}) $stop; + if ({qc,qo,q0} != {6'd1,1'b1,1'b1}) $stop; + if ({wc,wo,w0} != {7'd1,1'b1,1'b1}) $stop; + end + if (cyc==6) begin + l <= 16'b0000100100000000; + q <= 50'h01_00000100; + w <= 80'h01_00000100_00000000; + // + if ({lc,lo,l0} != {5'd1,1'b1,1'b1}) $stop; + if ({qc,qo,q0} != {6'd1,1'b1,1'b1}) $stop; + if ({wc,wo,w0} != {7'd1,1'b1,1'b1}) $stop; + end + if (cyc==7) begin + // + if ({lc,lo,l0} != {5'd2,1'b0,1'b0}) $stop; + if ({qc,qo,q0} != {6'd2,1'b0,1'b0}) $stop; + if ({wc,wo,w0} != {7'd2,1'b0,1'b0}) $stop; + end + if (cyc==8) begin + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + + final begin + $write("Goodbye world, at cycle %0d\n", cyc); + end + +endmodule diff --git a/test_regress/t/t_math_svl2.pl b/test_regress/t/t_math_svl2.pl new file mode 100755 index 000000000..908ee10cd --- /dev/null +++ b/test_regress/t/t_math_svl2.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_svl2.v b/test_regress/t/t_math_svl2.v new file mode 100644 index 000000000..4db302ed8 --- /dev/null +++ b/test_regress/t/t_math_svl2.v @@ -0,0 +1,41 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2006 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + // New number format + if ('0 !== {66{1'b0}}) $stop; + if ('1 !== {66{1'b1}}) $stop; + if ('x !== {66{1'bx}}) $stop; + if ('z !== {66{1'bz}}) $stop; +`ifndef nc // NC-Verilog 5.50-s09 chokes on this test + if ("\v" != 8'd11) $stop; + if ("\f" != 8'd12) $stop; + if ("\a" != 8'd7) $stop; + if ("\x9a" != 8'h9a) $stop; + if ("\xf1" != 8'hf1) $stop; +`endif + end + if (cyc==8) begin + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_math_tri.pl b/test_regress/t/t_math_tri.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_math_tri.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_tri.v b/test_regress/t/t_math_tri.v new file mode 100644 index 000000000..a98737793 --- /dev/null +++ b/test_regress/t/t_math_tri.v @@ -0,0 +1,26 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/); + + reg [3:0] a; + reg [99:0] x; + + initial begin + a = 4'b010x; + if (a[3:2] !== 2'b01) $stop; + if (|a !== 1'b1) $stop; + if (&a !== 1'b0) $stop; + x = 100'bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_math_vgen.pl b/test_regress/t/t_math_vgen.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_math_vgen.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_vgen.v b/test_regress/t/t_math_vgen.v new file mode 100644 index 000000000..76d9aec40 --- /dev/null +++ b/test_regress/t/t_math_vgen.v @@ -0,0 +1,278 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + reg check; initial check = 1'b0; + + // verilator lint_off WIDTH + + //============================================================ + + reg [ 1:0] W0095; //=3 + reg [ 58:0] W0101; //=0000000FFFFFFFF + always @(posedge clk) begin + if (cyc==1) begin + W0095 = ((2'h3)); + W0101 = ({27'h0,({16{(W0095)}})}); + end + end + always @(posedge clk) begin + if (cyc==2) begin + if ((W0101) != (59'h0FFFFFFFF)) if (check) $stop; + end + end + + //============================================================ + + reg [ 0:0] W1243; //=1 + always @(posedge clk) begin + if (cyc==1) begin + W1243 = ((1'h1)); + end + end + always @(posedge clk) begin + if (cyc==2) begin + // Width violation, but still... + if (((-W1243) < 32'h01) != (1'h0)) if (check) $stop; + if (({32{W1243}} < 32'h01) != (1'h0)) if (check) $stop; + end + end + + //============================================================ + + reg [ 0:0] W0344; //=0 + always @(posedge clk) begin + if (cyc==1) begin + W0344 = 1'b0; + end + end + always @(posedge clk) begin + if (cyc==2) begin + if ((W0344) != (1'h0)) if (check) $stop; + if (({116{(((- 95'h7FFFFFFFFFFFFFFFFFFFFFFF) ^ 95'h7FFFFFFFFFFFFFFFFFFFFFFF ) == ({94'h0,W0344}))}})) if (check) $stop; + end + end + + //============================================================ + + reg [ 63:0] W0372; //=FFFFFFFFFFFFFFFF + reg [118:0] W0420; //=7FFFFFFFFFFFFFFFFFFFFFFFFFFFFF + reg [115:0] W0421; //=00000000000000000000000000000 + always @(posedge clk) begin + if (cyc==1) begin + W0372 = ({64{((1'h1))}}); + W0421 = 116'h0; + W0420 = ({119{((W0372) <= (W0372))}}); + end + end + always @(posedge clk) begin + if (cyc==2) begin + if ((W0420[(- (W0421[115:110]))]) != (1'h1)) if (check) $stop; + end + end + + //============================================================ + + // gcc_2_96_bug + reg [ 31:0] W0161; //=FFFFFFFF + reg [ 62:0] W0217; //=0000000000000000 + reg [ 53:0] W0219; //=00000000000000 + always @(posedge clk) begin + if (cyc==1) begin + W0161 = 32'hFFFFFFFF; + W0217 = 63'h0; + W0219 = 54'h0; + end + end + always @(posedge clk) begin + if (cyc==2) begin + if ((W0161) != (32'hFFFFFFFF)) if (check) $stop; + if (((- (W0161)) & ((W0217[62:31]) & ({25'h0,(W0219[53:47])}))) != (32'h00000000)) if (check) $stop; + end + end + + //============================================================ + + reg [119:0] W0592; //=000000000000000000000000000000 + reg [ 7:0] W0593; //=70 + always @(posedge clk) begin + if (cyc==1) begin + W0593 = (((8'h90)) * ((8'hFF))); + W0592 = 120'h000000000000000000000000000000; + end + end + always @(posedge clk) begin + if (cyc==2) begin + if (((W0592[119:9]) >> ((W0593))) != (111'h0000000000000000000000000000)) if (check) $stop; + end + end + + //============================================================ + + reg [127:0] WA1063 ; //=00000000000000000000000000000001 + reg [ 34:0] WA1064 /*verilator public*/; //=7FFFFFFFF + reg [ 62:0] WA1065 ; //=0000000000000000 + reg [ 89:0] WA1066 /*verilator public*/; //=00000000000000000000001 + reg [ 34:0] WA1067 ; //=7FFFFFFFF + reg [111:0] WA1068; + + always @(check) begin + WA1067 = (~ (35'h0)); + WA1066 = (90'h00000000000000000000001); + WA1065 = (WA1066[89:27]); + WA1064 = (WA1067); + WA1063 = (~ ((~ (128'hffffffffffffffffffffffffffffffff)) ^ (~ (128'h00000000000000000000000000000001)))); + end + always @(posedge clk) begin + if (cyc==2) begin + if ((WA1063[(WA1064[(WA1065[((5'h04) | (5'h0))+:4])+:3])+:112]) != 112'h0) if (check) $stop; + end + end + + //============================================================ + + reg [127:0] WB1063 ; //=00000000000000000000000000000001 + reg [ 34:0] WB1064 /*verilator public*/; //=7FFFFFFFF + reg [ 62:0] WB1065 ; //=0000000000000000 + reg [ 89:0] WB1066 /*verilator public*/; //=00000000000000000000001 + reg [ 34:0] WB1067 ; //=7FFFFFFFF + reg [111:0] WB1068; + + always @(posedge clk) begin + if (cyc==1) begin + WB1067 = (~ (35'h0)); + WB1066 = (90'h00000000000000000000001); + end + if (cyc==2) WB1065 <= (WB1066[89:27]); + if (cyc==3) WB1064 <= (WB1067); + if (cyc==4) WB1063 <= (~ ((~ (128'hffffffffffffffffffffffffffffffff)) ^ (~ (128'h00000000000000000000000000000001)))); + if (cyc==5) WB1068 <= (WB1063[(WB1064[(WB1065[((5'h04) | (5'h0))+:4])+:3])+:112]); + end + always @(posedge clk) begin + if (cyc==9) begin + if (WB1068 != 112'h0) if (check) $stop; + if ((WB1063[(WB1064[(WB1065[((5'h04) | (5'h0))+:4])+:3])+:112]) != 112'h0) if (check) $stop; + end + end + + //============================================================ + + reg signed [ 60:0] WC0064 ; //=1FFFFFFFFFFFFFFF + reg signed [ 6:0] WC0065 ; //=00 + reg signed [ 62:0] WC0067 /*verilator public*/; //=33250A3BFFFFFFFF + + always @(check) begin + WC0064 = 61'sh1FFFFFFFFFFFFFFF; + WC0065 = 7'sh0; + if (((WC0064) >>> (WC0065)) != 61'sh1fffffffffffffff) if (check) $stop; + end + + //============================================================ + + reg signed [ 76:0] W0234 ; //=00000000000000000000 + reg signed [ 7:0] W0235 /*verilator public*/; //=B6 + always @(check) begin + W0235 = 8'shb6; + W0234 = ((77'sh0001ffffffffffffffff) >>> (W0235)); + if ((W0234) != 77'sh0) if (check) $stop; + end + + //============================================================ + + reg signed [ 30:0] W0146 ; //=00000001 + always @(check) begin : Block71 + W0146 = (31'sh00000001); + if ((W0146 >>> 6'sh3f) != 31'sh0) if (check) $stop; + end + + //============================================================ + + reg signed [ 54:0] W0857 /*verilator public*/; //=7FFFFFFFFFFFFF + + always @(check) begin : Block405 + W0857 = 55'sh7fffffffffffff; + if ((63'sh7fffffffffffffff >>> (W0857[54:54] ? 7'sh56 : 7'sh7f)) != 63'sh7fffffffffffffff) if (check) $stop; + end + + //============================================================ + + always @(posedge clk) begin + if ((((122'sh3ffffffffffffffd3e48e0900000001 >>> 8'shff) >>> 8'b1) ) != 122'sh3ffffffffffffffffffffffffffffff) if (check) $stop; + if (((95'sh7fff_ffff_ffffffff_ffffffff < 95'sh4a76_3d8b_0f4e3995_1146e342) != 1'h0)) if (check) $stop; + end + + //============================================================ + + reg signed [ 82:0] W0226 ; //=47A4301EE3FB4133EE3DA + + always @* begin : Block144 + W0226 = 83'sh47A4301EE3FB4133EE3DA; + if ((W0226 >>> 8'sh1a) != 83'sh7ffffff1e90c07b8fed04) if (check) $stop; + end + + //============================================================ + + reg signed [ 68:0] W0792 /*verilator public*/; //=169351569551247E0C + reg signed [ 68:0] W0793 ; //=1FFFFFFFFF4EB1A91A + + always @(posedge clk) begin + W0793 <= 69'sh1f_ffffffff_4eb1a91a; + W0792 <= (W0793 * 69'sh1F_0E989F3E_F15F509E); + if (W0792 != 69'sh16_93515695_51247E0C) if (check) $stop; + end + + //============================================================ + + reg signed [ 2:0] DW0515 /*verilator public*/; //=7 + + always @(posedge clk) begin + DW0515 <= 3'sh7; + if ($signed({62'h0,DW0515[1'h1]}) != 63'sh0000000000000001) if (check) $stop; + end + + //============================================================ + + reg signed [ 62:0] W0753 ; //=004E20004ED93E26 + reg [ 2:0] W0772 /*verilator public*/; //=7 + + always @(posedge clk) begin + W0753 <= 63'sh004E20004ED93E26; //(63'sh7fffffffffffffff + (63'sh464eac8c4ed93e27 & (63'sh08cf6243ffffffff))); + W0772 <= 3'h7; + if ((W0772[(W0753 < 63'sh0876c66a7e29fabf)]) != 1'h1) if (check) $stop; + if ((W0772[(63'sh004E20004ED93E26 < 63'sh0876c66a7e29fabf)]) != 1'h1) if (check) $stop; + end + + //============================================================ + + reg [ 98:0] W1027 ; //=7FFFFFFFFFFFFFFFFFFFFFFFF + always @(posedge clk) begin + W1027 <= ~99'h0; + // verilator lint_off CMPCONST + if (((1'sb1 < (95'sh7fffffffffffffffffffffff >= 95'sh09deb904ffffffffe062d44c))) != 1'h0) if (check) $stop; + // verilator lint_on CMPCONST + end + + //============================================================ + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==18) begin + check <= 1'b1; + end + if (cyc==20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_math_vliw.pl b/test_regress/t/t_math_vliw.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_math_vliw.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_vliw.v b/test_regress/t/t_math_vliw.v new file mode 100644 index 000000000..1df656ec9 --- /dev/null +++ b/test_regress/t/t_math_vliw.v @@ -0,0 +1,104 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=0; + reg [7:0] crc; + reg [223:0] sum; + + wire [255:0] mglehy = {32{~crc}}; + wire [215:0] drricx = {27{crc}}; + wire [15:0] apqrli = {2{~crc}}; + wire [2:0] szlfpf = crc[2:0]; + wire [15:0] dzosui = {2{crc}}; + wire [31:0] zndrba = {16{crc[1:0]}}; + wire [223:0] bxiouf; + + vliw vliw ( + // Outputs + .bxiouf (bxiouf), + // Inputs + .mglehy (mglehy[255:0]), + .drricx (drricx[215:0]), + .apqrli (apqrli[15:0]), + .szlfpf (szlfpf[2:0]), + .dzosui (dzosui[15:0]), + .zndrba (zndrba[31:0])); + + always @ (posedge clk) begin + cyc <= cyc + 1; + crc <= {crc[6:0], ~^ {crc[7],crc[5],crc[4],crc[3]}}; + if (cyc==0) begin + // Setup + crc <= 8'hed; + sum <= 224'h0; + end + else if (cyc<90) begin + //$write("[%0t] cyc==%0d BXI=%x\n",$time, cyc, bxiouf); + sum <= {sum[222:0],sum[223]} ^ bxiouf; + end + else if (cyc==99) begin + $write("[%0t] cyc==%0d crc=%b %x\n",$time, cyc, crc, sum); + if (crc != 8'b01110000) $stop; + if (sum != 224'h1fdff998855c3c38d467e28124847831f9ad6d4a09f2801098f032a8) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module vliw ( + input[255:0] mglehy, + input[215:0] drricx, + input[15:0] apqrli, + input[2:0] szlfpf, + input[15:0] dzosui, + input[31:0] zndrba, + output [223:0] bxiouf + ); + + wire [463:0] zhknfc = ({29{~apqrli}} & {mglehy, drricx[215:8]}) + | ({29{apqrli}} & {mglehy[247:0], drricx}); + wire [335:0] umntwz = ({21{~dzosui}} & zhknfc[463:128]) + | ({21{dzosui}} & zhknfc[335:0]); + wire [335:0] viuvoc = umntwz << {szlfpf, 4'b0000}; + wire [223:0] rzyeut = viuvoc[335:112]; + wire [223:0] bxiouf = {rzyeut[7:0], + rzyeut[15:8], + rzyeut[23:16], + rzyeut[31:24], + rzyeut[39:32], + rzyeut[47:40], + rzyeut[55:48], + rzyeut[63:56], + rzyeut[71:64], + rzyeut[79:72], + rzyeut[87:80], + rzyeut[95:88], + rzyeut[103:96], + rzyeut[111:104], + rzyeut[119:112], + rzyeut[127:120], + rzyeut[135:128], + rzyeut[143:136], + rzyeut[151:144], + rzyeut[159:152], + rzyeut[167:160], + rzyeut[175:168], + rzyeut[183:176], + rzyeut[191:184], + rzyeut[199:192], + rzyeut[207:200], + rzyeut[215:208], + rzyeut[223:216]}; + +endmodule diff --git a/test_regress/t/t_mem.pl b/test_regress/t/t_mem.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_mem.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem.v b/test_regress/t/t_mem.v new file mode 100644 index 000000000..195ad9f12 --- /dev/null +++ b/test_regress/t/t_mem.v @@ -0,0 +1,64 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [7:0] memory8_16 [15:0]; + + reg m_we; + reg [3:1] m_addr; + reg [15:0] m_data; + + always @ (posedge clk) begin + // Load instructions from cache + memory8_16[{m_addr,1'd0}] <= 8'hfe; + if (m_we) begin + {memory8_16[{m_addr,1'd1}], + memory8_16[{m_addr,1'd0}]} <= m_data; + end + end + + always @ (posedge clk) begin + m_we <= 0; + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + m_we <= 1'b1; + m_addr <= 3'd2; + m_data <= 16'h55_44; + end + if (cyc==2) begin + m_we <= 1'b1; + m_addr <= 3'd3; + m_data <= 16'h77_66; + end + if (cyc==3) begin + m_we <= 0; // Check we really don't write this + m_addr <= 3'd3; + m_data <= 16'h0bad; + end + if (cyc==5) begin + if (memory8_16[4] != 8'h44) $stop; + if (memory8_16[5] != 8'h55) $stop; + if (memory8_16[6] != 8'hfe) $stop; + if (memory8_16[7] != 8'h77) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_mem_fifo.pl b/test_regress/t/t_mem_fifo.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_mem_fifo.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem_fifo.v b/test_regress/t/t_mem_fifo.v new file mode 100644 index 000000000..071c70b5d --- /dev/null +++ b/test_regress/t/t_mem_fifo.v @@ -0,0 +1,108 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2006 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; initial cyc=0; + reg [63:0] crc; + + wire [65:0] outData; // From fifo of fifo.v + wire [15:0] inData = crc[15:0]; + wire [1:0] inWordPtr = crc[17:16]; + wire wrEn = crc[20]; + wire [1:0] wrPtr = crc[33:32]; + wire [1:0] rdPtr = crc[34:33]; + + fifo fifo ( + // Outputs + .outData (outData[65:0]), + // Inputs + .clk (clk), + .inWordPtr (inWordPtr[1:0]), + .inData (inData[15:0]), + .rdPtr (rdPtr), + .wrPtr (wrPtr), + .wrEn (wrEn)); + + always @ (posedge clk) begin + //$write("[%0t] cyc==%0d crc=%b q=%x\n",$time, cyc, crc, outData); + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63]^crc[2]^crc[0]}; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + end + else if (cyc==90) begin + if (outData[63:0] != 64'hd9bcbc276f0984ea) $stop; + end + else if (cyc==91) begin + if (outData[63:0] != 64'hef77cd9b13a866f0) $stop; + end + else if (cyc==92) begin + if (outData[63:0] != 64'h2750cd9b13a866f0) $stop; + end + else if (cyc==93) begin + if (outData[63:0] != 64'h4ea0bc276f0984ea) $stop; + end + else if (cyc==94) begin + if (outData[63:0] != 64'h9d41bc276f0984ea) $stop; + end + else if (cyc==99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module fifo (/*AUTOARG*/ + // Outputs + outData, + // Inputs + clk, inWordPtr, inData, wrPtr, rdPtr, wrEn + ); + + parameter fifoDepthLog2 = 1; + parameter fifoDepth = 1<1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem_file.v b/test_regress/t/t_mem_file.v new file mode 100644 index 000000000..e1aeb4f9b --- /dev/null +++ b/test_regress/t/t_mem_file.v @@ -0,0 +1,166 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=0; + reg [63:0] crc; + reg [63:0] sum; + + wire r1_en /*verilator public*/ = crc[12]; + wire [1:0] r1_ad /*verilator public*/ = crc[9:8]; + wire r2_en /*verilator public*/ = 1'b1; + wire [1:0] r2_ad /*verilator public*/ = crc[11:10]; + wire w1_en /*verilator public*/ = crc[5]; + wire [1:0] w1_a /*verilator public*/ = crc[1:0]; + wire [63:0] w1_d /*verilator public*/ = {2{crc[63:32]}}; + wire w2_en /*verilator public*/ = crc[4]; + wire [1:0] w2_a /*verilator public*/ = crc[3:2]; + wire [63:0] w2_d /*verilator public*/ = {2{~crc[63:32]}}; + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [63:0] r1_d_d2r; // From file of file.v + wire [63:0] r2_d_d2r; // From file of file.v + // End of automatics + + file file (/*AUTOINST*/ + // Outputs + .r1_d_d2r (r1_d_d2r[63:0]), + .r2_d_d2r (r2_d_d2r[63:0]), + // Inputs + .clk (clk), + .r1_en (r1_en), + .r1_ad (r1_ad[1:0]), + .r2_en (r2_en), + .r2_ad (r2_ad[1:0]), + .w1_en (w1_en), + .w1_a (w1_a[1:0]), + .w1_d (w1_d[63:0]), + .w2_en (w2_en), + .w2_a (w2_a[1:0]), + .w2_d (w2_d[63:0])); + + always @ (posedge clk) begin + //$write("[%0t] cyc==%0d EN=%b%b%b%b R0=%x R1=%x\n",$time, cyc, r1_en,r2_en,w1_en,w2_en, r1_d_d2r, r2_d_d2r); + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63]^crc[2]^crc[0]}; + sum <= {r1_d_d2r ^ r2_d_d2r} ^ {sum[62:0],sum[63]^sum[2]^sum[0]}; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + end + else if (cyc<10) begin + // We've manually verified all X's are out of the design by this point + sum <= 64'h0; + end + else if (cyc<90) begin + end + else if (cyc==99) begin + $write("*-* All Finished *-*\n"); + $write("[%0t] cyc==%0d crc=%x %x\n",$time, cyc, crc, sum); + if (crc != 64'hc77bb9b3784ea091) $stop; + if (sum != 64'h5e9ea8c33a97f81e) $stop; + $finish; + end + end + +endmodule + +module file (/*AUTOARG*/ + // Outputs + r1_d_d2r, r2_d_d2r, + // Inputs + clk, r1_en, r1_ad, r2_en, r2_ad, w1_en, w1_a, w1_d, w2_en, w2_a, w2_d + ); + + input clk; + input r1_en; + input [1:0] r1_ad; + output [63:0] r1_d_d2r; + input r2_en; + input [1:0] r2_ad; + output [63:0] r2_d_d2r; + input w1_en; + input [1:0] w1_a; + input [63:0] w1_d; + input w2_en; + input [1:0] w2_a; + input [63:0] w2_d; + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + // End of automatics + /*AUTOREG*/ + // Beginning of automatic regs (for this module's undeclared outputs) + reg [63:0] r1_d_d2r; + reg [63:0] r2_d_d2r; + // End of automatics + + // Writes + wire [3:0] m_w1_onehotwe = ({4{w1_en}} & (4'b1 << w1_a)); + wire [3:0] m_w2_onehotwe = ({4{w2_en}} & (4'b1 << w2_a)); + + wire [63:0] rg0_wrdat = m_w1_onehotwe[0] ? w1_d : w2_d; + wire [63:0] rg1_wrdat = m_w1_onehotwe[1] ? w1_d : w2_d; + wire [63:0] rg2_wrdat = m_w1_onehotwe[2] ? w1_d : w2_d; + wire [63:0] rg3_wrdat = m_w1_onehotwe[3] ? w1_d : w2_d; + + wire [3:0] m_w_onehotwe = m_w1_onehotwe | m_w2_onehotwe; + + // Storage + reg [63:0] m_rg0_r; + reg [63:0] m_rg1_r; + reg [63:0] m_rg2_r; + reg [63:0] m_rg3_r; + + always @ (posedge clk) begin + if (m_w_onehotwe[0]) m_rg0_r <= rg0_wrdat; + if (m_w_onehotwe[1]) m_rg1_r <= rg1_wrdat; + if (m_w_onehotwe[2]) m_rg2_r <= rg2_wrdat; + if (m_w_onehotwe[3]) m_rg3_r <= rg3_wrdat; + end + + // Reads + reg [1:0] m_r1_ad_d1r; + reg [1:0] m_r2_ad_d1r; + reg [1:0] m_ren_d1r; + + always @ (posedge clk) begin + if (r1_en) m_r1_ad_d1r <= r1_ad; + if (r2_en) m_r2_ad_d1r <= r2_ad; + m_ren_d1r <= {r2_en, r1_en}; + end + + // Scheme1: shift... + wire [3:0] m_r1_onehot_d1 = (4'b1 << m_r1_ad_d1r); + // Scheme2: bit mask + reg [3:0] m_r2_onehot_d1; + always @* begin + m_r2_onehot_d1 = 4'd0; + m_r2_onehot_d1[m_r2_ad_d1r] = 1'b1; + end + + wire [63:0] m_r1_d_d1 = (({64{m_r1_onehot_d1[0]}} & m_rg0_r) | + ({64{m_r1_onehot_d1[1]}} & m_rg1_r) | + ({64{m_r1_onehot_d1[2]}} & m_rg2_r) | + ({64{m_r1_onehot_d1[3]}} & m_rg3_r)); + + wire [63:0] m_r2_d_d1 = (({64{m_r2_onehot_d1[0]}} & m_rg0_r) | + ({64{m_r2_onehot_d1[1]}} & m_rg1_r) | + ({64{m_r2_onehot_d1[2]}} & m_rg2_r) | + ({64{m_r2_onehot_d1[3]}} & m_rg3_r)); + + always @ (posedge clk) begin + if (m_ren_d1r[0]) r1_d_d2r <= m_r1_d_d1; + if (m_ren_d1r[1]) r2_d_d2r <= m_r2_d_d1; + end + +endmodule diff --git a/test_regress/t/t_mem_iforder.pl b/test_regress/t/t_mem_iforder.pl new file mode 100755 index 000000000..24012256c --- /dev/null +++ b/test_regress/t/t_mem_iforder.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem_iforder.v b/test_regress/t/t_mem_iforder.v new file mode 100644 index 000000000..b2e16f87f --- /dev/null +++ b/test_regress/t/t_mem_iforder.v @@ -0,0 +1,103 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2006 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; initial cyc=0; + reg [63:0] crc; + reg [31:0] sum; + + wire [15:0] out0; + wire [15:0] out1; + wire [15:0] inData = crc[15:0]; + wire wr0a = crc[16]; + wire wr0b = crc[17]; + wire wr1a = crc[18]; + wire wr1b = crc[19]; + + fifo fifo ( + // Outputs + .out0 (out0[15:0]), + .out1 (out1[15:0]), + // Inputs + .clk (clk), + .wr0a (wr0a), + .wr0b (wr0b), + .wr1a (wr1a), + .wr1b (wr1b), + .inData (inData[15:0])); + + always @ (posedge clk) begin + //$write("[%0t] cyc==%0d crc=%x q=%x\n",$time, cyc, crc, sum); + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63]^crc[2]^crc[0]}; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + sum <= 32'h0; + end + else if (cyc>10 && cyc<90) begin + sum <= {sum[30:0],sum[31]} ^ {out1, out0}; + end + else if (cyc==99) begin + if (sum != 32'he8bbd130) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module fifo (/*AUTOARG*/ + // Outputs + out0, out1, + // Inputs + clk, wr0a, wr0b, wr1a, wr1b, inData + ); + + input clk; + input wr0a; + input wr0b; + input wr1a; + input wr1b; + input [15:0] inData; + + output [15:0] out0; + output [15:0] out1; + + reg [15:0] mem [1:0]; + reg [15:0] memtemp2 [1:0]; + reg [15:0] memtemp3 [1:0]; + + assign out0 = {mem[0] ^ memtemp2[0]}; + assign out1 = {mem[1] ^ memtemp3[1]}; + + always @(posedge clk) begin + // These mem assignments must be done in order after processing + if (wr0a) begin + memtemp2[0] <= inData; + mem[0] <= inData; + end + if (wr0b) begin + memtemp3[0] <= inData; + mem[0] <= ~inData; + end + if (wr1a) begin + memtemp3[1] <= inData; + mem[1] <= inData; + end + if (wr1b) begin + memtemp2[1] <= inData; + mem[1] <= ~inData; + end + end + +endmodule diff --git a/test_regress/t/t_mem_multi_io_bad.pl b/test_regress/t/t_mem_multi_io_bad.pl new file mode 100755 index 000000000..c000a520f --- /dev/null +++ b/test_regress/t/t_mem_multi_io_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>$Last_Self->{v3}, + nc=>0, + expect=> +'%Error: t/t_mem_multi_io_bad.v:\d+: Arrayed variables may not be inputs nor outputs +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem_multi_io_bad.v b/test_regress/t/t_mem_multi_io_bad.v new file mode 100644 index 000000000..429a7526d --- /dev/null +++ b/test_regress/t/t_mem_multi_io_bad.v @@ -0,0 +1,14 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Outputs + dim1 + ); + reg [1:0] dim1 [1:0]; + output dim1; // Bad, can't output multi-dim +endmodule + diff --git a/test_regress/t/t_mem_multi_ref_bad.pl b/test_regress/t/t_mem_multi_ref_bad.pl new file mode 100755 index 000000000..21012cdec --- /dev/null +++ b/test_regress/t/t_mem_multi_ref_bad.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>$Last_Self->{v3}, + nc=>0, # Need to get it not to give the prompt + expect=> +'%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; variable already selected, or bad dimension +%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; variable already selected, or bad dimension +%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; variable already selected, or bad dimension +%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; variable already selected, or bad dimension +%Error: Exiting due to.*', + ); + +ok(1); +1; + diff --git a/test_regress/t/t_mem_multi_ref_bad.v b/test_regress/t/t_mem_multi_ref_bad.v new file mode 100644 index 000000000..ed8449042 --- /dev/null +++ b/test_regress/t/t_mem_multi_ref_bad.v @@ -0,0 +1,23 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/); + reg [1:0] dim0; + reg [1:0] dim1 [1:0]; + reg [1:0] dim2 [1:0][1:0]; + reg dim0nv[1:0]; + + initial begin + dim0[1][1] = 0; // Bad: Not arrayed + dim1[1][1][1] = 0; // Bad: Not arrayed to right depth + dim2[1][1][1] = 0; // OK + dim2[1][1:0] = 0; // Bad: Bitsel too soon + dim0nv[1:0] = 0; // Bad: Not vectored + dim0nv[1][1] = 0; // Bad: Not arrayed to right depth + end + +endmodule + diff --git a/test_regress/t/t_mem_multidim.pl b/test_regress/t/t_mem_multidim.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_mem_multidim.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem_multidim.v b/test_regress/t/t_mem_multidim.v new file mode 100644 index 000000000..e9c4d56ca --- /dev/null +++ b/test_regress/t/t_mem_multidim.v @@ -0,0 +1,92 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + // verilator lint_off BLKANDNBLK + // 3 3 4 + reg [71:0] memw [2:0][1:3][5:2]; + reg [7:0] memn [2:0][1:3][5:2]; + // verilator lint_on BLKANDNBLK + + integer cyc; initial cyc=0; + reg [63:0] crc; + reg [71:0] wide; + reg [7:0] narrow; + reg [1:0] index0; + reg [1:0] index1; + reg [2:0] index2; + integer i0,i1,i2; + + integer imem[2:0][1:3]; + + initial begin + for (i0=0; i0<3; i0=i0+1) begin + for (i1=1; i1<4; i1=i1+1) begin + imem[i0[1:0]] [i1[1:0]] = i1; + for (i2=2; i2<6; i2=i2+1) begin + memw[i0[1:0]] [i1[1:0]] [i2[2:0]] = {56'hfe_fee0_fee0_fee0_,4'b0,i0[3:0],i1[3:0],i2[3:0]}; + memn[i0[1:0]] [i1[1:0]] [i2[2:0]] = 8'b1000_0001; + end + end + end + end + + reg [71:0] wread; + + always @ (posedge clk) begin + //$write("cyc==%0d crc=%x i[%d][%d][%d] nar=%x wide=%x\n",cyc, crc, index0,index1,index2, narrow, wide); + cyc <= cyc + 1; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + narrow <= 8'h0; + wide <= 72'h0; + index0 <= 2'b0; + index1 <= 2'b0; + index2 <= 3'b0; + end + else if (cyc<90) begin + index0 <= crc[1:0]; + index1 <= crc[3:2]; + index2 <= crc[6:4]; + crc <= {crc[62:0], crc[63]^crc[2]^crc[0]}; + // We never read past bounds, or get unspecific results + // We also never read lowest indexes, as writing outside of range may corrupt them + if (index0>=0+1 && index0<=2 && index1>=1+1 && index1<=3 && index2>=2+1 && index2<=5) begin + narrow <= ({narrow[6:0], narrow[7]^narrow[0]} + ^ {memn[index0][index1][index2]}); + wread = memw[index0][index1][index2]; + wide <= ({wide[70:0], wide[71]^wide[2]^wide[0]} ^ wread); + //$write("Get memw[%d][%d][%d] -> %x\n",index0,index1,index2, wread); + end + // We may write past bounds of memory + memn[index0][index1][index2] [crc[10:8]+:3] <= crc[2:0]; + memn[index0][index1][index2] <= {~crc[6:0],crc[7]}; + memw[index0][index1][index2] <= {~crc[7:0],crc}; + //$write("Set memw[%d][%d][%d] <= %x\n",index0,index1,index2, {~crc[7:0],crc}); + end + else if (cyc==90) begin + memn[0][1][3] <= memn[0][1][3] ^ 8'ha8; + end + else if (cyc==91) begin + end + else if (cyc==99) begin + $write("[%0t] cyc==%0d crc=%x nar=%x wide=%x\n",$time, cyc, crc, narrow, wide); + if (crc != 64'h65e3bddcd9bc2750) $stop; + if (narrow != 8'hca) $stop; + if (wide != 72'h4edafed31ba6873f73) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_mem_multidim_Ox.pl b/test_regress/t/t_mem_multidim_Ox.pl new file mode 100755 index 000000000..60d63e715 --- /dev/null +++ b/test_regress/t/t_mem_multidim_Ox.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +top_filename("t/t_mem_multidim.v"); + +compile ( + v_flags2 => [$Last_Self->{v3}?'--Ox':''], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem_multidim_trace.pl b/test_regress/t/t_mem_multidim_trace.pl new file mode 100755 index 000000000..7f197ea27 --- /dev/null +++ b/test_regress/t/t_mem_multidim_trace.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +top_filename("t/t_mem_multidim.v"); + +compile ( + v_flags2 => [$Last_Self->{v3}?'--sp --trace':''], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem_multiwire.pl b/test_regress/t/t_mem_multiwire.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_mem_multiwire.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem_multiwire.v b/test_regress/t/t_mem_multiwire.v new file mode 100644 index 000000000..e04d6362a --- /dev/null +++ b/test_regress/t/t_mem_multiwire.v @@ -0,0 +1,84 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + wire [7:0] array [2:0][1:3]; + + integer cyc; initial cyc=0; + integer i0,i1,i2; + genvar g0,g1,g2; + + generate + for (g0=0; g0<3; g0=g0+1) begin + for (g1=1; g1<4; g1=g1+1) begin + inst inst (.q(array[g0[1:0]] [g1[1:0]]), + .cyc(cyc), + .i0(g0[1:0]), + .i1(g1[1:0])); + end + end + endgenerate + + always @ (posedge clk) begin + //$write("cyc==%0d\n",cyc); + cyc <= cyc + 1; + if (cyc==2) begin + if (array[2][1] !== 8'h92) $stop; + for (i0=0; i0<3; i0=i0+1) begin + for (i1=1; i1<4; i1=i1+1) begin + //$write(" array[%0d][%0d] == 8'h%x\n",i0,i1,array[i0[1:0]] [i1[1:0]]); + if (array[i0[1:0]] [i1[1:0]] != {i0[1:0], i1[1:0], cyc[3:0]}) $stop; + end + end + end + else if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module inst (/*AUTOARG*/ + // Outputs + q, + // Inputs + cyc, i0, i1 + ); + output reg [7:0] q; + input [31:0] cyc; + input [1:0] i0; + input [1:0] i1; + + inst2 inst2 (/*AUTOINST*/ + // Inputs + .cyc (cyc[31:0]), + .i0 (i0[1:0]), + .i1 (i1[1:0])); + + always @* begin + q = {i0, i1, cyc[3:0]}; + end +endmodule + +module inst2 (/*AUTOARG*/ + // Inputs + cyc, i0, i1 + ); + /*verilator no_inline_module*/ // So we'll get a CELL under a GENFOR, without inlining + input [31:0] cyc; + input [1:0] i0; + input [1:0] i1; + initial begin + if (cyc==32'h1) $write("[%0t] i0=%d i1=%d\n", $time, i0, i1); + end +endmodule diff --git a/test_regress/t/t_mem_shift.pl b/test_regress/t/t_mem_shift.pl new file mode 100755 index 000000000..7b2256408 --- /dev/null +++ b/test_regress/t/t_mem_shift.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + v_flags2 => ["--stats"], + ); + +if ($Last_Self->{v3}) { + file_grep ($Last_Self->{stats}, qr/Optimizations, Delayed shared-sets\s+14/i); +} + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem_shift.v b/test_regress/t/t_mem_shift.v new file mode 100644 index 000000000..939c2492f --- /dev/null +++ b/test_regress/t/t_mem_shift.v @@ -0,0 +1,62 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2006 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; initial cyc=0; + reg [63:0] crc; + + integer i; + reg [63:0] mem [7:0]; + + always @ (posedge clk) begin + if (cyc==1) begin + for (i=0; i<8; i=i+1) begin + mem[i] <= 64'h0; + end + end + else begin + mem[0] <= crc; + for (i=1; i<8; i=i+1) begin + mem[i] <= mem[i-1]; + end + end + end + + wire [63:0] outData = mem[7]; + + always @ (posedge clk) begin + //$write("[%0t] cyc==%0d crc=%b q=%x\n",$time, cyc, crc, outData); + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63]^crc[2]^crc[0]}; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + end + else if (cyc==90) begin + if (outData != 64'h1265e3bddcd9bc27) $stop; + end + else if (cyc==91) begin + if (outData != 64'h24cbc77bb9b3784e) $stop; + end + else if (cyc==92) begin + end + else if (cyc==93) begin + end + else if (cyc==94) begin + end + else if (cyc==99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_mem_slot.cpp b/test_regress/t/t_mem_slot.cpp new file mode 100644 index 000000000..5c5f864f7 --- /dev/null +++ b/test_regress/t/t_mem_slot.cpp @@ -0,0 +1,52 @@ +#include +#include +#include "Vt_mem_slot.h" + +unsigned int Array[3]; + +unsigned int +StepSim (Vt_mem_slot *sim, unsigned int slot, unsigned int bit, unsigned int val, unsigned int rslot) { + printf ("StepSim: slot=%d bit=%d val=%d rslot=%d\n", slot, bit, val, rslot); + + sim->SlotIdx = slot; + sim->BitToChange = bit; + sim->BitVal = val; + sim->SlotToReturn = rslot; + sim->Clk = 0; + + sim->eval(); + + sim->Clk = 1; + + sim->eval(); + + + if (sim->OutputVal != Array[rslot]) { + printf ("%%Error: got %x - expected %x\n", sim->OutputVal, Array[rslot]); + exit (1); + } + + if (val) + Array[slot] |= (1 << bit); + else + Array[slot] &= ~(1 << bit); + + return sim->OutputVal; +} + +int main (int argc, char *argv[]) { + Vt_mem_slot *sim = new Vt_mem_slot; + int slot, bit, i; + + Verilated::debug(0); + + /* clear all bits in the array */ + for (slot = 0; slot < 3; slot++) + for (bit = 0; bit < 2; bit++) + StepSim (sim, slot, bit, 0, 0); + + printf ("\nTesting\n"); + for (i = 0; i < 100; i++) + StepSim (sim, random() % 3, random() % 2, random() % 2, random() % 3); + printf ("*-* All Finished *-*\n"); +} diff --git a/test_regress/t/t_mem_slot.pl b/test_regress/t/t_mem_slot.pl new file mode 100755 index 000000000..401bfcafa --- /dev/null +++ b/test_regress/t/t_mem_slot.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + make_top_shell => 0, + make_main => 0, + v_flags2 => ["--exe t/$Last_Self->{name}.cpp"], + ) if $Last_Self->{v3}; + +execute ( + check_finished=>1, + ) if $Last_Self->{v3}; +ok(1); +1; diff --git a/test_regress/t/t_mem_slot.v b/test_regress/t/t_mem_slot.v new file mode 100644 index 000000000..9c5805185 --- /dev/null +++ b/test_regress/t/t_mem_slot.v @@ -0,0 +1,26 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2006 by Wilson Snyder. + +`define RegDel 1 + +module t_mem_slot (Clk, SlotIdx, BitToChange, BitVal, SlotToReturn, OutputVal); + + input Clk; + input [1:0] SlotIdx; + input BitToChange; + input BitVal; + input [1:0] SlotToReturn; + output [1:0] OutputVal; + + reg [1:0] Array[0:2]; + + always @(posedge Clk) + begin + Array[SlotIdx][BitToChange] <= #`RegDel BitVal; + + OutputVal = Array[SlotToReturn]; + end +endmodule diff --git a/test_regress/t/t_mod_bad_twotop.pl b/test_regress/t/t_mod_bad_twotop.pl new file mode 100755 index 000000000..5d60d4b35 --- /dev/null +++ b/test_regress/t/t_mod_bad_twotop.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>$Last_Self->{v3}, + nc=>0, # Need to get it not to give the prompt + expect=> +'%Error: t/t_mod_bad_twotop.v:\d+: Unsupported: Multiple top level modules: t2 and t +%Error: Exiting due to.*', + ); + +ok(1); +1; + diff --git a/test_regress/t/t_mod_bad_twotop.v b/test_regress/t/t_mod_bad_twotop.v new file mode 100644 index 000000000..670fb28ec --- /dev/null +++ b/test_regress/t/t_mod_bad_twotop.v @@ -0,0 +1,19 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t; + initial begin + $write("Two top modules\n"); + $stop; + end +endmodule + +module t2; + initial begin + $write("Two top modules\n"); + $stop; + end +endmodule diff --git a/test_regress/t/t_order.pl b/test_regress/t/t_order.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_order.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order.v b/test_regress/t/t_order.v new file mode 100644 index 000000000..aa6e3fedd --- /dev/null +++ b/test_regress/t/t_order.v @@ -0,0 +1,112 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + // surefire lint_off ASWEBB + // surefire lint_off ASWEMB + // surefire lint_off STMINI + // surefire lint_off CSEBEQ + + input clk; + + reg [7:0] a_to_clk_levm3; + reg [7:0] b_to_clk_levm1; + reg [7:0] c_com_levs10; + reg [7:0] d_to_clk_levm2; + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [7:0] m_from_clk_lev1_r; // From a of t_order_a.v + wire [7:0] n_from_clk_lev2; // From a of t_order_a.v + wire [7:0] o_from_com_levs11; // From a of t_order_a.v + wire [7:0] o_from_comandclk_levs12;// From a of t_order_a.v + wire [7:0] o_subfrom_clk_lev2; // From b of t_order_b.v + // End of automatics + + reg [7:0] cyc; initial cyc=0; + + t_order_a a ( + .one (8'h1), + /*AUTOINST*/ + // Outputs + .m_from_clk_lev1_r (m_from_clk_lev1_r[7:0]), + .n_from_clk_lev2 (n_from_clk_lev2[7:0]), + .o_from_com_levs11 (o_from_com_levs11[7:0]), + .o_from_comandclk_levs12(o_from_comandclk_levs12[7:0]), + // Inputs + .clk (clk), + .a_to_clk_levm3 (a_to_clk_levm3[7:0]), + .b_to_clk_levm1 (b_to_clk_levm1[7:0]), + .c_com_levs10 (c_com_levs10[7:0]), + .d_to_clk_levm2 (d_to_clk_levm2[7:0])); + + t_order_b b ( + /*AUTOINST*/ + // Outputs + .o_subfrom_clk_lev2 (o_subfrom_clk_lev2[7:0]), + // Inputs + .m_from_clk_lev1_r (m_from_clk_lev1_r[7:0])); + + reg [7:0] o_from_com_levs12; + reg [7:0] o_from_com_levs13; + always @ (/*AS*/o_from_com_levs11) begin + o_from_com_levs12 = o_from_com_levs11 + 8'h1; + o_from_com_levs12 = o_from_com_levs12 + 8'h1; // Test we can add to self and optimize + o_from_com_levs13 = o_from_com_levs12; + end + + reg sepassign_in; + // verilator lint_off UNOPTFLAT + wire [3:0] sepassign; + // verilator lint_on UNOPTFLAT + + // verilator lint_off UNOPT + assign #0.1 sepassign[0] = 0, + sepassign[1] = sepassign[2], + sepassign[2] = sepassign[3], + sepassign[3] = sepassign_in; + wire [7:0] o_subfrom_clk_lev3 = o_subfrom_clk_lev2; + // verilator lint_on UNOPT + + always @ (posedge clk) begin + cyc <= cyc+8'd1; + sepassign_in <= 0; + if (cyc == 8'd1) begin + a_to_clk_levm3 <= 0; + d_to_clk_levm2 <= 1; + b_to_clk_levm1 <= 1; + c_com_levs10 <= 2; + sepassign_in <= 1; + end + if (cyc == 8'd2) begin + if (sepassign !== 4'b1110) $stop; + end + if (cyc == 8'd3) begin + + $display("%d %d %d %d",m_from_clk_lev1_r, + n_from_clk_lev2, + o_from_com_levs11, + o_from_comandclk_levs12); + + if (m_from_clk_lev1_r !== 8'h2) $stop; + if (o_subfrom_clk_lev3 !== 8'h2) $stop; + if (n_from_clk_lev2 !== 8'h2) $stop; + if (o_from_com_levs11 !== 8'h3) $stop; + if (o_from_com_levs13 !== 8'h5) $stop; + if (o_from_comandclk_levs12 !== 8'h5) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_order_a.v b/test_regress/t/t_order_a.v new file mode 100644 index 000000000..63ee364de --- /dev/null +++ b/test_regress/t/t_order_a.v @@ -0,0 +1,57 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t_order_a (/*AUTOARG*/ + // Outputs + m_from_clk_lev1_r, n_from_clk_lev2, o_from_com_levs11, o_from_comandclk_levs12, + // Inputs + clk, a_to_clk_levm3, b_to_clk_levm1, c_com_levs10, d_to_clk_levm2, one + ); + + input clk; + input [7:0] a_to_clk_levm3; + input [7:0] b_to_clk_levm1; + input [7:0] c_com_levs10; + input [7:0] d_to_clk_levm2; + input [7:0] one; + output [7:0] m_from_clk_lev1_r; + output [7:0] n_from_clk_lev2; + output [7:0] o_from_com_levs11; + output [7:0] o_from_comandclk_levs12; + + /*AUTOREG*/ + // Beginning of automatic regs (for this module's undeclared outputs) + reg [7:0] m_from_clk_lev1_r; + // End of automatics + + // surefire lint_off ASWEBB + // surefire lint_off ASWEMB + + wire [7:0] a_to_clk_levm1; + wire [7:0] a_to_clk_levm2; + wire [7:0] c_com_levs11; + reg [7:0] o_from_comandclk_levs12; + wire [7:0] n_from_clk_lev2; + wire [7:0] n_from_clk_lev3; + + assign a_to_clk_levm1 = a_to_clk_levm2 + d_to_clk_levm2; + assign a_to_clk_levm2 = a_to_clk_levm3 + 0; + + always @ (posedge clk) begin + m_from_clk_lev1_r <= a_to_clk_levm1 + b_to_clk_levm1; + end + + assign c_com_levs11 = c_com_levs10 + one; + always @ (/*AS*/c_com_levs11 or n_from_clk_lev3) o_from_comandclk_levs12 = c_com_levs11 + n_from_clk_lev3; + assign n_from_clk_lev2 = m_from_clk_lev1_r; + assign n_from_clk_lev3 = n_from_clk_lev2; + wire [7:0] o_from_com_levs11 = c_com_levs10 + 1; + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_order_b.v b/test_regress/t/t_order_b.v new file mode 100644 index 000000000..730e403a3 --- /dev/null +++ b/test_regress/t/t_order_b.v @@ -0,0 +1,23 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t_order_b (/*AUTOARG*/ + // Outputs + o_subfrom_clk_lev2, + // Inputs + m_from_clk_lev1_r + ); + + input [7:0] m_from_clk_lev1_r; + output [7:0] o_subfrom_clk_lev2; + + wire [7:0] o_subfrom_clk_lev2 = m_from_clk_lev1_r; + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_order_clkinst.pl b/test_regress/t/t_order_clkinst.pl new file mode 100755 index 000000000..65c0146de --- /dev/null +++ b/test_regress/t/t_order_clkinst.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +my $fail = ($Last_Self->{v3} && verilator_version() !~ /\(ord\)/); + +compile ( + ); + +execute ( + check_finished => !$fail, + fails => $fail, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_clkinst.v b/test_regress/t/t_order_clkinst.v new file mode 100644 index 000000000..85e896ec4 --- /dev/null +++ b/test_regress/t/t_order_clkinst.v @@ -0,0 +1,117 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + // verilator lint_off COMBDLY + // verilator lint_off UNOPT + // verilator lint_off UNOPTFLAT + + reg c1_start; initial c1_start = 0; + wire [31:0] c1_count; + comb_loop c1 (.count(c1_count), .start(c1_start)); + + wire s2_start = (c1_count==0 && c1_start); + wire [31:0] s2_count; + seq_loop s2 (.count(s2_count), .start(s2_start)); + + wire c3_start = (s2_count[0]); + wire [31:0] c3_count; + comb_loop c3 (.count(c3_count), .start(c3_start)); + + reg [7:0] cyc; initial cyc=0; + always @ (posedge clk) begin + //$write("[%0t] %x counts %x %x %x\n",$time,cyc,c1_count,s2_count,c3_count); + cyc <= cyc + 8'd1; + case (cyc) + 8'd00: begin + c1_start <= 1'b0; + end + 8'd01: begin + c1_start <= 1'b1; + end + default: ; + endcase + case (cyc) + 8'd02: begin + if (c1_count!=32'h3) $stop; + if (s2_count!=32'h3) $stop; + if (c3_count!=32'h6) $stop; + end + 8'd03: begin + $write("*-* All Finished *-*\n"); + $finish; + end + default: ; + endcase + end +endmodule + +module comb_loop (/*AUTOARG*/ + // Outputs + count, + // Inputs + start + ); + input start; + output reg [31:0] count; initial count = 0; + + reg [31:0] runnerm1, runner; initial runner = 0; + + always @ (start) begin + if (start) begin + runner = 3; + end + end + + always @ (/*AS*/runner) begin + runnerm1 = runner - 32'd1; + end + + always @ (/*AS*/runnerm1) begin + if (runner > 0) begin + count = count + 1; + runner = runnerm1; + $write ("%m count=%d runner =%x\n",count, runnerm1); + end + end + +endmodule + +module seq_loop (/*AUTOARG*/ + // Outputs + count, + // Inputs + start + ); + input start; + output reg [31:0] count; initial count = 0; + + reg [31:0] runnerm1, runner; initial runner = 0; + + always @ (start) begin + if (start) begin + runner <= 3; + end + end + + always @ (/*AS*/runner) begin + runnerm1 = runner - 32'd1; + end + + always @ (/*AS*/runnerm1) begin + if (runner > 0) begin + count = count + 1; + runner <= runnerm1; + $write ("%m count=%d runner<=%x\n",count, runnerm1); + end + end + +endmodule diff --git a/test_regress/t/t_order_comboclkloop.pl b/test_regress/t/t_order_comboclkloop.pl new file mode 100755 index 000000000..6be58e3e5 --- /dev/null +++ b/test_regress/t/t_order_comboclkloop.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +compile ( + ); + +execute ( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_comboclkloop.v b/test_regress/t/t_order_comboclkloop.v new file mode 100644 index 000000000..c52bf5677 --- /dev/null +++ b/test_regress/t/t_order_comboclkloop.v @@ -0,0 +1,69 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + // verilator lint_off BLKANDNBLK + // verilator lint_off COMBDLY + // verilator lint_off UNOPT + // verilator lint_off UNOPTFLAT + // verilator lint_off MULTIDRIVEN + + reg [31:0] runnerm1, runner; initial runner = 0; + reg [31:0] runcount; initial runcount = 0; + reg [31:0] clkrun; initial clkrun = 0; + reg [31:0] clkcount; initial clkcount = 0; + always @ (/*AS*/runner) begin + runnerm1 = runner - 32'd1; + end + reg run0; + always @ (/*AS*/runnerm1) begin + if ((runner & 32'hf)!=0) begin + runcount = runcount + 1; + runner = runnerm1; + $write (" seq runcount=%0d runner =%0x\n",runcount, runnerm1); + end + run0 = (runner[8:4]!=0 && runner[3:0]==0); + end + + always @ (posedge run0) begin + // Do something that forces another combo run + clkcount <= clkcount + 1; + runner[8:4] <= runner[8:4] - 1; + runner[3:0] <= 3; + $write ("[%0t] posedge runner=%0x\n", $time, runner); + end + + reg [7:0] cyc; initial cyc=0; + always @ (posedge clk) begin + $write("[%0t] %x counts %0x %0x\n",$time,cyc,runcount,clkcount); + cyc <= cyc + 8'd1; + case (cyc) + 8'd00: begin + runner <= 0; + end + 8'd01: begin + runner <= 32'h35; + end + default: ; + endcase + case (cyc) + 8'd02: begin + if (runcount!=32'he) $stop; + if (clkcount!=32'h3) $stop; + end + 8'd03: begin + $write("*-* All Finished *-*\n"); + $finish; + end + default: ; + endcase + end +endmodule diff --git a/test_regress/t/t_order_comboloop.pl b/test_regress/t/t_order_comboloop.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_order_comboloop.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_comboloop.v b/test_regress/t/t_order_comboloop.v new file mode 100644 index 000000000..5c695d027 --- /dev/null +++ b/test_regress/t/t_order_comboloop.v @@ -0,0 +1,56 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + integer cyc; initial cyc=1; + + // verilator lint_off UNOPT + // verilator lint_off UNOPTFLAT + reg [31:0] runner; initial runner = 5; + reg [31:0] runnerm1; + reg [59:0] runnerq; + reg [89:0] runnerw; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin +`ifdef verilator + if (runner != 0) $stop; // Initial settlement failed +`endif + end + if (cyc==2) begin + runner = 20; + runnerq = 60'h0; + runnerw = 90'h0; + end + if (cyc==3) begin + if (runner != 0) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + + // This forms a "loop" where we keep going through the always till runner=0 + // This isn't "regular" beh code, but insures our change detection is working properly + always @ (/*AS*/runner) begin + runnerm1 = runner - 32'd1; + end + + always @ (/*AS*/runnerm1) begin + if (runner > 0) begin + runner = runnerm1; + runnerq = runnerq - 60'd1; + runnerw = runnerw - 90'd1; + $write ("[%0t] runner=%d\n", $time, runner); + end + end + +endmodule diff --git a/test_regress/t/t_order_doubleloop.pl b/test_regress/t/t_order_doubleloop.pl new file mode 100755 index 000000000..272a6722c --- /dev/null +++ b/test_regress/t/t_order_doubleloop.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +my $fail = ($Last_Self->{v3} && verilator_version() !~ /\(ord\)/); + +compile ( + ); + +execute ( + check_finished => !$fail, + fails => $fail, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_doubleloop.v b/test_regress/t/t_order_doubleloop.v new file mode 100644 index 000000000..bc741be59 --- /dev/null +++ b/test_regress/t/t_order_doubleloop.v @@ -0,0 +1,100 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + integer cyc; initial cyc=1; + + // verilator lint_off UNOPT + // verilator lint_off UNOPTFLAT + // verilator lint_off MULTIDRIVEN + // verilator lint_off BLKANDNBLK + + reg [31:0] comcnt; + reg [31:0] dlycnt; initial dlycnt=0; + reg [31:0] lastdlycnt; initial lastdlycnt = 0; + + reg [31:0] comrun; initial comrun = 0; + reg [31:0] comrunm1; + reg [31:0] dlyrun; initial dlyrun = 0; + reg [31:0] dlyrunm1; + always @ (posedge clk) begin + $write("[%0t] cyc %d\n",$time,cyc); + cyc <= cyc + 1; + if (cyc==2) begin + // Test # of iters + lastdlycnt = 0; + comcnt = 0; + dlycnt <= 0; + end + if (cyc==3) begin + dlyrun <= 5; + dlycnt <= 0; + end + if (cyc==4) begin + comrun = 4; + end + end + always @ (negedge clk) begin + if (cyc==5) begin + $display("%d %d\n", dlycnt, comcnt); + if (dlycnt != 32'd5) $stop; + if (comcnt != 32'd19) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + + // This forms a "loop" where we keep going through the always till comrun=0 + reg runclk; initial runclk = 1'b0; + always @ (/*AS*/comrunm1 or dlycnt) begin + if (lastdlycnt != dlycnt) begin + comrun = 3; + $write ("[%0t] comrun=%0d start\n", $time, comrun); + end + else if (comrun > 0) begin + comrun = comrunm1; + if (comrunm1==1) begin + runclk = 1; + $write ("[%0t] comrun=%0d [trigger clk]\n", $time, comrun); + end + else $write ("[%0t] comrun=%0d\n", $time, comrun); + end + lastdlycnt = dlycnt; + end + + always @ (/*AS*/comrun) begin + if (comrun!=0) begin + comrunm1 = comrun - 32'd1; + comcnt = comcnt + 32'd1; + $write("[%0t] comcnt=%0d\n",$time,comcnt); + end + end + + // This forms a "loop" where we keep going through the always till dlyrun=0 + reg runclkrst; + always @ (posedge runclk) begin + runclkrst <= 1; + $write ("[%0t] runclk\n", $time); + if (dlyrun > 0) begin + dlyrun <= dlyrun - 32'd1; + dlycnt <= dlycnt + 32'd1; + $write ("[%0t] dlyrun<=%0d\n", $time, dlyrun-32'd1); + end + end + + always @* begin + if (runclkrst) begin + $write ("[%0t] runclk reset\n", $time); + runclkrst = 0; + runclk = 0; + end + end + +endmodule diff --git a/test_regress/t/t_order_multialways.pl b/test_regress/t/t_order_multialways.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_order_multialways.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_multialways.v b/test_regress/t/t_order_multialways.v new file mode 100644 index 000000000..3d3f8c27d --- /dev/null +++ b/test_regress/t/t_order_multialways.v @@ -0,0 +1,62 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [31:0] in_a; + reg [31:0] in_b; + + reg [31:0] e,f,g,h; + + always @ (/*AS*/in_a) begin + e = in_a; + f = {e[15:0], e[31:16]}; + g = {f[15:0], f[31:16]}; + h = {g[15:0], g[31:16]}; + end + + // verilator lint_off UNOPTFLAT + reg [31:0] e2,f2,g2,h2; + always @ (/*AS*/f2) begin + h2 = {g2[15:0], g2[31:16]}; + g2 = {f2[15:0], f2[31:16]}; + end + always @ (/*AS*/in_a) begin + f2 = {e2[15:0], e2[31:16]}; + e2 = in_a; + end + // verilator lint_on UNOPTFLAT + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + //$write("%d %x %x\n", cyc, h, h2); + if (h != h2) $stop; + if (cyc==1) begin + in_a <= 32'h89a14fab; + in_b <= 32'h7ab512fa; + end + if (cyc==2) begin + in_a <= 32'hf4c11a42; + in_b <= 32'h359967c6; + if (h != 32'h4fab89a1) $stop; + end + if (cyc==3) begin + if (h != 32'h1a42f4c1) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_order_wireloop.pl b/test_regress/t/t_order_wireloop.pl new file mode 100755 index 000000000..fdb4b5365 --- /dev/null +++ b/test_regress/t/t_order_wireloop.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2005 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. + +compile ( + fails=>$Last_Self->{v3}, + expect=> +'%Error: t/t_order_wireloop.v:\d+: Wire inputs its own output, creating circular logic .wire x=x. +', + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_wireloop.v b/test_regress/t/t_order_wireloop.v new file mode 100644 index 000000000..d42c93ce5 --- /dev/null +++ b/test_regress/t/t_order_wireloop.v @@ -0,0 +1,22 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// Use this file as a template for submitting bugs, etc. +// This module takes a single clock input, and should either +// $write("*-* All Finished *-*\n"); +// $finish +// on success, or $stop. +// +// **If you do not wish for your code to be released to the public +// please note it here** + +module t (/*AUTOARG*/); + + wire foo; + wire bar; + + // Oh dear. + assign foo = bar; + assign bar = foo; + +endmodule diff --git a/test_regress/t/t_param.pl b/test_regress/t/t_param.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_param.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_param.v b/test_regress/t/t_param.v new file mode 100644 index 000000000..ad55b3deb --- /dev/null +++ b/test_regress/t/t_param.v @@ -0,0 +1,59 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + parameter PAR = 3; + + m1 #(PAR) m1(); + m3 #(PAR) m3(); + + input clk; + integer cyc=1; + reg [4:0] bitsel; + + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc==0) begin + bitsel = 0; + if (PAR[bitsel]!==1'b1) $stop; + bitsel = 1; + if (PAR[bitsel]!==1'b1) $stop; + bitsel = 2; + if (PAR[bitsel]!==1'b0) $stop; + end + if (cyc==1) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module m1; + parameter PAR1 = 0; + m2 #(PAR1-1) m2 (); +endmodule + +module m2; + parameter PAR2 = 10; + initial begin + $display("%x",PAR2); + if (PAR2 !== 2) $stop; + end +endmodule + +module m3; + localparam LOC = 13; + parameter PAR = 10; + initial begin + $display("%x %x",LOC,PAR); + if (LOC !== 13) $stop; + if (PAR !== 3) $stop; + end +endmodule diff --git a/test_regress/t/t_param_long.pl b/test_regress/t/t_param_long.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_param_long.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_param_long.v b/test_regress/t/t_param_long.v new file mode 100644 index 000000000..792da0b3c --- /dev/null +++ b/test_regress/t/t_param_long.v @@ -0,0 +1,175 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + parameter PAR = 3; + input clk; + + defparam i.L00 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L01 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L02 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L03 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L04 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L05 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L06 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L07 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L08 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L09 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L0A = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L0B = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L0C = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L0D = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L0E = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L0F = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L10 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L11 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L12 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L13 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L14 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L15 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L16 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L17 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L18 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L19 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L1A = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L1B = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L1C = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L1D = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L1E = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L1F = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L20 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L21 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L22 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L23 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L24 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L25 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L26 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L27 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L28 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L29 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L2A = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L2B = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L2C = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L2D = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L2E = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L2F = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L30 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L31 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L32 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L33 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L34 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L35 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L36 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L37 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L38 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L39 = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L3A = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L3B = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L3C = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L3D = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L3E = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.L3F = 256'h000012300000000000000000000000000000000000000000000000000000cdef; + defparam i.A0 = "HELLO_WORLD_BOY_THIS_IS_LONG"; + defparam i.A1 = "HELLO_WORLD_BOY_THIS_IS_LONG"; + defparam i.A2 = "HELLO_WORLD_BOY_THIS_IS_LONG"; + + i i (.clk(clk)); + + integer cyc=1; + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc==1) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module i + (/*AUTOARG*/ + // Inputs + clk + ); + + // verilator public_module + + input clk; + + parameter [255:0] L00 = 256'h0; + parameter [255:0] L01 = 256'h0; + parameter [255:0] L02 = 256'h0; + parameter [255:0] L03 = 256'h0; + parameter [255:0] L04 = 256'h0; + parameter [255:0] L05 = 256'h0; + parameter [255:0] L06 = 256'h0; + parameter [255:0] L07 = 256'h0; + parameter [255:0] L08 = 256'h0; + parameter [255:0] L09 = 256'h0; + parameter [255:0] L0A = 256'h0; + parameter [255:0] L0B = 256'h0; + parameter [255:0] L0C = 256'h0; + parameter [255:0] L0D = 256'h0; + parameter [255:0] L0E = 256'h0; + parameter [255:0] L0F = 256'h0; + parameter [255:0] L10 = 256'h0; + parameter [255:0] L11 = 256'h0; + parameter [255:0] L12 = 256'h0; + parameter [255:0] L13 = 256'h0; + parameter [255:0] L14 = 256'h0; + parameter [255:0] L15 = 256'h0; + parameter [255:0] L16 = 256'h0; + parameter [255:0] L17 = 256'h0; + parameter [255:0] L18 = 256'h0; + parameter [255:0] L19 = 256'h0; + parameter [255:0] L1A = 256'h0; + parameter [255:0] L1B = 256'h0; + parameter [255:0] L1C = 256'h0; + parameter [255:0] L1D = 256'h0; + parameter [255:0] L1E = 256'h0; + parameter [255:0] L1F = 256'h0; + parameter [255:0] L20 = 256'h0; + parameter [255:0] L21 = 256'h0; + parameter [255:0] L22 = 256'h0; + parameter [255:0] L23 = 256'h0; + parameter [255:0] L24 = 256'h0; + parameter [255:0] L25 = 256'h0; + parameter [255:0] L26 = 256'h0; + parameter [255:0] L27 = 256'h0; + parameter [255:0] L28 = 256'h0; + parameter [255:0] L29 = 256'h0; + parameter [255:0] L2A = 256'h0; + parameter [255:0] L2B = 256'h0; + parameter [255:0] L2C = 256'h0; + parameter [255:0] L2D = 256'h0; + parameter [255:0] L2E = 256'h0; + parameter [255:0] L2F = 256'h0; + parameter [255:0] L30 = 256'h0; + parameter [255:0] L31 = 256'h0; + parameter [255:0] L32 = 256'h0; + parameter [255:0] L33 = 256'h0; + parameter [255:0] L34 = 256'h0; + parameter [255:0] L35 = 256'h0; + parameter [255:0] L36 = 256'h0; + parameter [255:0] L37 = 256'h0; + parameter [255:0] L38 = 256'h0; + parameter [255:0] L39 = 256'h0; + parameter [255:0] L3A = 256'h0; + parameter [255:0] L3B = 256'h0; + parameter [255:0] L3C = 256'h0; + parameter [255:0] L3D = 256'h0; + parameter [255:0] L3E = 256'h0; + parameter [255:0] L3F = 256'h0; + parameter [255:0] A0 = 256'h0; + parameter [255:0] A1 = 256'h0; + parameter [255:0] A2 = 256'h0; + + always @ (posedge clk) begin + end +endmodule diff --git a/test_regress/t/t_param_named.pl b/test_regress/t/t_param_named.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_param_named.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_param_named.v b/test_regress/t/t_param_named.v new file mode 100644 index 000000000..301c52549 --- /dev/null +++ b/test_regress/t/t_param_named.v @@ -0,0 +1,66 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + parameter PAR = 3; + input clk; + + defparam m3.FROMDEFP = 19; + + m3 #(.P3(PAR), + .P2(2)) + m3(.clk(clk)); + + integer cyc=1; + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc==1) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module m3 +`ifdef verilator + #( + parameter UNCH = 99; + parameter P1 = 10; + parameter P2 = 20, + P3 = 30; + ) +`endif + (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + localparam LOC = 13; + +`ifndef verilator // Vcs not compliant yet + parameter UNCH = 99; + parameter P1 = 10; + parameter P2 = 20; + parameter P3 = 30; +`endif + + parameter FROMDEFP = 11; + + initial begin + $display("%x %x %x",P1,P2,P3); + end + always @ (posedge clk) begin + if (UNCH !== 99) $stop; + if (P1 !== 10) $stop; + if (P2 !== 2) $stop; + if (P3 !== 3) $stop; + if (FROMDEFP !== 19) $stop; + end +endmodule diff --git a/test_regress/t/t_param_named_2.pl b/test_regress/t/t_param_named_2.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_param_named_2.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_param_named_2.v b/test_regress/t/t_param_named_2.v new file mode 100644 index 000000000..c06a093c9 --- /dev/null +++ b/test_regress/t/t_param_named_2.v @@ -0,0 +1,56 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + parameter PAR = 3; + input clk; + + m3 m3_inst (.clk(clk)); + defparam m3_inst.FROMDEFP = 19; + defparam m3_inst.P2 = 2; + //defparam m3_inst.P3 = PAR; + defparam m3_inst.P3 = 3; + + integer cyc=1; + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc==1) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module m3 + (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + localparam LOC = 13; + + parameter UNCH = 99; + parameter P1 = 10; + parameter P2 = 20; + parameter P3 = 30; + + parameter FROMDEFP = 11; + + initial begin + $display("%x %x %x",P1,P2,P3); + end + always @ (posedge clk) begin + if (UNCH !== 99) $stop; + if (P1 !== 10) $stop; + if (P2 !== 2) $stop; + if (P3 !== 3) $stop; + if (FROMDEFP !== 19) $stop; + end +endmodule diff --git a/test_regress/t/t_param_repl.pl b/test_regress/t/t_param_repl.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_param_repl.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_param_repl.v b/test_regress/t/t_param_repl.v new file mode 100644 index 000000000..89483d8ef --- /dev/null +++ b/test_regress/t/t_param_repl.v @@ -0,0 +1,49 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + parameter [31:0] TWENTY4 = 24; + parameter [31:0] PA = TWENTY4/8; + parameter [1:0] VALUE = 2'b10; + parameter [5:0] REPL = {PA{VALUE}}; + parameter [7:0] CONC = {REPL,VALUE}; + + parameter DBITS = 32; + parameter INIT_BYTE = 8'h1F; + parameter DWORDS_LOG2 = 7; + parameter DWORDS = (1<1, + expect=> +'%Error: t/t_pp_misdef_bad.v:11: Define or directive not defined: `NOTDEF +%Error: Exiting due to.*', + ) if $Last_Self->{v3}; + +ok(1); +1; + diff --git a/test_regress/t/t_pp_misdef_bad.v b/test_regress/t/t_pp_misdef_bad.v new file mode 100644 index 000000000..90474ae64 --- /dev/null +++ b/test_regress/t/t_pp_misdef_bad.v @@ -0,0 +1,17 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t; +`define A B + + // NOTDEF isn't defined here: + `NOTDEF + +//`include "notfound" + + initial $stop; // Should have failed + +endmodule diff --git a/test_regress/t/t_preproc.out b/test_regress/t/t_preproc.out new file mode 100644 index 000000000..f903729c3 --- /dev/null +++ b/test_regress/t/t_preproc.out @@ -0,0 +1,151 @@ + +`line 1 "t/t_preproc.v" 1 + + + + + + + + + +`line 9 "t/t_preproc.v" 0 + +`line 1 "t/t_preproc_inc2.v" 1 + + + +At file t/t_preproc_inc2.v line 4 + + +`line 6 "t/t_preproc_inc2.v" 0 + +`line 1 "t/t_preproc_inc3.v" 1 + + + + + + + + + At file inc3_a_filename_from_line_directive line 10 + + + +`line 13 "inc3_a_filename_from_line_directive" 0 + + + + + +`line 17 "inc3_a_filename_from_line_directive" 0 + + +`line 18 "inc3_a_filename_from_line_directive" 2 + +`line 6 "t/t_preproc_inc2.v" 0 + + +`line 7 "t/t_preproc_inc2.v" 2 + +`line 9 "t/t_preproc.v" 0 + + + + + +/*verilator pass_thru comment*/ + +/*verilator pass_thru_comment2*/ + + + + + + + + wire [3:0] q = { + 1'b1 +`line 25 "t/t_preproc.v" 0 + , + +`line 26 "t/t_preproc.v" 0 + 1'b0 , + 1'b1 +`line 27 "t/t_preproc.v" 0 + , + 1'b1 +`line 28 "t/t_preproc.v" 0 + + }; + +text. + + + + foo bar + foobar2 + + + + + + + + + + + + first part second part third part +Line_Preproc_Check 49 + + + + + + + + deep deep + + + +"Inside: `nosubst" + "`nosubst" + + + x y LLZZ x y + p q LLZZ p q r s LLZZ r s LLZZ p q LLZZ p q r s LLZZ r s + + + + firstline comma","line LLZZ firstline comma","line + + + x y LLZZ "a" y + + + (a,b) (a,b) + + +$display( "left side: \" right side\"" ) + + + bar_suffix + + + + $c("Zap(\"",bug1,"\");"); ; + $c("Zap(\"","bug2","\");"); ; + + + + + + + + +`line 95 "t/t_preproc.v" 0 + +Line_Preproc_Check 96 + diff --git a/test_regress/t/t_preproc.pl b/test_regress/t/t_preproc.pl new file mode 100755 index 000000000..ffbc57e3d --- /dev/null +++ b/test_regress/t/t_preproc.pl @@ -0,0 +1,60 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +my $stdout_filename = "obj_dir/$Last_Self->{name}__test.vpp"; + +if (!$Last_Self->{v3}) { + ok(1); +} else { + compile ( + v_flags2 => ['-DDEF_A0 -E'], + verilator_make_gcc=>0, + stdout_filename => $stdout_filename, + ); + ok(preproc_check("t/$Last_Self->{name}.v", $stdout_filename) + && files_identical($stdout_filename, "t/$Last_Self->{name}.out")); +} + +sub preproc_check { + my $filename1 = shift; + my $filename2 = shift; + + my @Line_Checks; + { # Read line comments. + my $fh = IO::File->new($filename1) or die "%Error: $! $filename1\n"; + while (defined(my $line = $fh->getline)) { + if ($line =~ /^Line_Preproc_Check/) { + push @Line_Checks, $.; + } + } + $fh->close; + } + { # See if output file agrees. + my $fh = IO::File->new($filename2) or die "%Error: $! $filename2\n"; + my $lineno = 0; + while (defined(my $line = $fh->getline)) { + $lineno++; + if ($line =~ /^\`line\s+(\d+)/) { + $lineno = $1 - 1; + } + if ($line =~ /^Line_Preproc_Check\s+(\d+)/) { + my $linecmt = $1; + my $check = shift @Line_Checks; + if (!$check) { $Last_Self->error("$filename2:$.: Extra Line_Preproc_Check\n"); } + if ($linecmt != $check) { $Last_Self->error("$filename2:$.: __LINE__ inserted $linecmt, exp=$check\n"); } + if ($lineno != $check) { $Last_Self->error("$filename2:$.: __LINE__ on `line $lineno, exp=$check\n"); } + } + } + $fh->close; + } + if ($Line_Checks[0]) { $Last_Self->error("$filename2: Missing a Line_Preproc_Check\n"); } + return 1; +} + +1; diff --git a/test_regress/t/t_preproc.v b/test_regress/t/t_preproc.v new file mode 100644 index 000000000..5ebea8eda --- /dev/null +++ b/test_regress/t/t_preproc.v @@ -0,0 +1,96 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004-2006 by Wilson Snyder. + +//=========================================================================== +// Includes +`include "t_preproc_inc2.v" + +//=========================================================================== +// Comments + +/* verilator pass_thru comment */ + +// verilator pass_thru_comment2 + +//=========================================================================== +// Defines + +`define DEF_A3 +`define DEF_A1 +// DEF_A0 set by command line + wire [3:0] q = { + `ifdef DEF_A3 1'b1 `else 1'b0 `endif , + `ifdef DEF_A2 1'b1 `else 1'b0 `endif , + `ifdef DEF_A1 1'b1 `else 1'b0 `endif , + `ifdef DEF_A0 1'b1 `else 1'b0 `endif + }; + +text. + +`define FOOBAR foo /*but not */ bar /* or this either */ +`define FOOBAR2 foobar2 // but not +`FOOBAR +`FOOBAR2 + +`define MULTILINE first part \ + second part \ + third part + +`define MOREMULTILINE {\ + a,\ + b,\ + c} + +/*******COMMENT*****/ +`MULTILINE +Line_Preproc_Check `__LINE__ + +//=========================================================================== + +`define syn_negedge_reset_l or negedge reset_l + +`define DEEP deep +`define DEEPER `DEEP `DEEP +`DEEPER + +`define nosubst NOT_SUBSTITUTED +`define WITHTICK "`nosubst" +"Inside: `nosubst" +`WITHTICK + +`define withparam(a, b) a b LLZZ a b +`withparam(x,y) +`withparam(`withparam(p,q),`withparam ( r , s )) + +`withparam(firstline + , + comma","line) + +`define withquote(a, bar) a bar LLZZ "a" bar +`withquote( x , y) + +`define noparam (a,b) +`noparam(a,b) + +`define msg(x,y) `"x: `\`"y`\`"`" +$display(`msg(left side, right side)) + +`define foo(f) f``_suffix +`foo(bar) + +`define zap(which) \ + $c("Zap(\"",which,"\");"); +`zap(bug1); +`zap("bug2"); + +//=========================================================================== +// Ifdef + +`define EMPTY_TRUE +`ifndef EMPTY_TRUE + `error "Empty is still true" +`endif +Line_Preproc_Check `__LINE__ diff --git a/test_regress/t/t_preproc_inc2.v b/test_regress/t/t_preproc_inc2.v new file mode 100644 index 000000000..e1604c763 --- /dev/null +++ b/test_regress/t/t_preproc_inc2.v @@ -0,0 +1,6 @@ +// DESCRIPTION: Verilog::Preproc: Example source code +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2000-2005 by Wilson Snyder. +At file `__FILE__ line `__LINE__ +`define INCFILE +`include `INCFILE diff --git a/test_regress/t/t_preproc_inc3.v b/test_regress/t/t_preproc_inc3.v new file mode 100644 index 000000000..3ea23368f --- /dev/null +++ b/test_regress/t/t_preproc_inc3.v @@ -0,0 +1,17 @@ +`line 2 "inc3_a_filename_from_line_directive" 0 +// DESCRIPTION: Verilog::Preproc: Example source code +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2000-2005 by Wilson Snyder. + +`ifndef _EXAMPLE_INC2_V_ + `define _EXAMPLE_INC2_V_ 1 + `define _EMPTY + // FOO + At file `__FILE__ line `__LINE__ +`else + `error "INC2 File already included once" +`endif // guard + +`ifdef not_defined + `include "NotToBeInced.v" +`endif diff --git a/test_regress/t/t_preproc_psl.v b/test_regress/t/t_preproc_psl.v new file mode 100644 index 000000000..6dc24e117 --- /dev/null +++ b/test_regress/t/t_preproc_psl.v @@ -0,0 +1,72 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +// verilator metacomment preserved +/**/ +/*verilator metacomment also_preserved*/ + +Hello in t_preproc_psl.v + // Psl capitalized not relevant + // Double commented ignored // psl not ok + // You can't have multiple statements on one // psl line. + // Inline /*cmt*/ comments not allowed inside psl comments + + // psl default clock = (posedge clk); + // psl fails1: cover {cyc==10}; + // psl assert always cyc!=10; + // psl assert always cyc==3 -> mask==8'h2; + // psl failsx: cover {cyc==3 && mask==8'h1}; + /* psl fails2: + cover { + cyc==3 && mask==8'h9}; + // Ignore this comment-in-between-statements (however not legal inside a statement) + fails3: always assert { + cyc==3 && mask==8'h10 }; + */ + `__LINE__ + + // Note the PSL statement can be on a unique line + // There can also be multiple "psl" keywords per line. + /* + psl + fails_ml: + assert always + cyc==3 -> mask==8'h21; + psl + fails_mlalso: assert always cyc==3 -> mask==8'h21; + */ + `__LINE__ + + // psl assert never (cyc==1 && reset_l); + + // psl fails3: assert always + // cyc==3 -> mask==8'h21; + // syntax_error, not_part_of_above_stmt; + +// We need to count { and ( when looking for ; that terminate a PSL expression + // psl assert always + // {[*]; cyc==3; + // cyc==4; cyc==6}; + // syntax_error, not_part_of_above_stmt; + +// However /**/ pairs can't be split as above. + +`ifdef NEVER + // psl ifdefs have precedence; +`endif + +// Macros are expanded... +`define define_sig cyc + // psl assert always `define_sig!=10; + +`ifdef verilator + `psl +psl assert always sig!=90; + `verilog +`endif + +// Did we end up right? +`__LINE__ diff --git a/test_regress/t/t_preproc_psl_off.out b/test_regress/t/t_preproc_psl_off.out new file mode 100644 index 000000000..2b6082377 --- /dev/null +++ b/test_regress/t/t_preproc_psl_off.out @@ -0,0 +1,77 @@ + +`line 1 "t/t_preproc_psl.v" 1 + + + + + + +/*verilator metacomment preserved*/ + +/*verilator metacomment also_preserved*/ + +Hello in t_preproc_psl.v + + + + + + + + + + + + + + + + + + 29 + + + + + + + + + + + + 41 + + + + + + + + + + + + + + + + + + +`line 59 "t/t_preproc_psl.v" 0 + + + + + + + + `psl +psl assert always sig!=90; + `verilog + + + +72 + diff --git a/test_regress/t/t_preproc_psl_off.pl b/test_regress/t/t_preproc_psl_off.pl new file mode 100755 index 000000000..590d78e61 --- /dev/null +++ b/test_regress/t/t_preproc_psl_off.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +my $stdout_filename = "obj_dir/$Last_Self->{name}__test.vpp"; + +top_filename("t/t_preproc_psl.v"); + +if (!$Last_Self->{v3}) { + ok(1); +} else { + compile ( + v_flags2 => ['-E'], + verilator_make_gcc=>0, + stdout_filename => $stdout_filename, + ); + ok(files_identical($stdout_filename, "t/$Last_Self->{name}.out")); +} + +1; diff --git a/test_regress/t/t_preproc_psl_on.out b/test_regress/t/t_preproc_psl_on.out new file mode 100644 index 000000000..9cc8f7927 --- /dev/null +++ b/test_regress/t/t_preproc_psl_on.out @@ -0,0 +1,77 @@ + +`line 1 "t/t_preproc_psl.v" 1 + + + + + + +/*verilator metacomment preserved*/ + +/*verilator metacomment also_preserved*/ + +Hello in t_preproc_psl.v + + + + + + psl default clock = (posedge clk); + psl fails1: cover {cyc==10}; + psl assert always cyc!=10; + psl assert always cyc==3 -> mask==8'h2; + psl failsx: cover {cyc==3 && mask==8'h1}; + psl fails2: + cover { + cyc==3 && mask==8'h9}; + + fails3: always assert { + cyc==3 && mask==8'h10 }; + + 29 + + + + + psl + fails_ml: + assert always + cyc==3 -> mask==8'h21; + psl + fails_mlalso: assert always cyc==3 -> mask==8'h21; + + 41 + + psl assert never (cyc==1 && reset_l); + + psl fails3: assert always + psl cyc==3 -> mask==8'h21; + + + + psl assert always + psl {[*]; cyc==3; + psl cyc==4; cyc==6}; + + + + + + + +`line 59 "t/t_preproc_psl.v" 0 + + + + + psl assert always cyc !=10; + + + `psl +psl assert always sig!=90; + `verilog + + + +72 + diff --git a/test_regress/t/t_preproc_psl_on.pl b/test_regress/t/t_preproc_psl_on.pl new file mode 100755 index 000000000..0ec30d544 --- /dev/null +++ b/test_regress/t/t_preproc_psl_on.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +my $stdout_filename = "obj_dir/$Last_Self->{name}__test.vpp"; + +top_filename("t/t_preproc_psl.v"); + +if (!$Last_Self->{v3}) { + ok(1); +} else { + compile ( + v_flags2 => ['-psl -E'], + verilator_make_gcc=>0, + stdout_filename => $stdout_filename, + ); + ok(files_identical($stdout_filename, "t/$Last_Self->{name}.out")); +} + +1; diff --git a/test_regress/t/t_psl_basic.pl b/test_regress/t/t_psl_basic.pl new file mode 100755 index 000000000..ed1003927 --- /dev/null +++ b/test_regress/t/t_psl_basic.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +compile ( + v_flags2 => [$Last_Self->{v3}?'--assert':($Last_Self->{nc}?'+assert':'')], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_psl_basic.v b/test_regress/t/t_psl_basic.v new file mode 100644 index 000000000..11387ff22 --- /dev/null +++ b/test_regress/t/t_psl_basic.v @@ -0,0 +1,55 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg toggle; + + integer cyc; initial cyc=1; + wire [7:0] cyc_copy = cyc[7:0]; + + // psl cover {cyc==3 || cyc==4} @ (posedge clk); + // psl assert {cyc<100} @ (posedge clk) report "AssertionFalse1"; +`ifdef FAILING_ASSERTIONS + // psl assert {toggle} @ (posedge clk) report "AssertionShouldFail"; +`endif + + // psl default clock = negedge clk; +//FIX // psl assert always {cyc<99}; + // psl cover {cyc==9} report "DefaultClock,expect=1"; + + // psl assert {(cyc==5)->toggle}; + // psl cover {(cyc==5)->toggle} report "ToggleLogIf,expect=1"; +`ifdef NOT_SUP + // psl assert {toggle<->cyc[0]}; + // psl cover {toggle<->cyc[0]} report "CycsLogIff,expect=10"; +`endif + + // Test {{..}} == Sequence of sequence... + // psl assert {{true}}; + + always @ (negedge clk) begin + //if (!(cyc==5) || toggle) $write("%d: %s\n", cyc, "ToggleLogIf,expect=1"); + //if (toggle&&cyc[0] || ~toggle&&~cyc[0]) $write("%d: %s\n", cyc, "CycsLogIff,expect=10"); + end + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + toggle <= !cyc[0]; + if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_psl_basic_cover.pl b/test_regress/t/t_psl_basic_cover.pl new file mode 100755 index 000000000..d9fa8a951 --- /dev/null +++ b/test_regress/t/t_psl_basic_cover.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +top_filename("t/t_psl_basic.v"); + +compile ( + v_flags2 => [$Last_Self->{v3}?'--psl --sp --coverage-user':''], + ); + +execute ( + check_finished=>1, + ); + +file_grep ($Last_Self->{coverage_filename}, qr/t=>'psl_cover',o=>'cover',c=>2\);/); +file_grep ($Last_Self->{coverage_filename}, qr/DefaultClock.*,c=>1\);/); +file_grep ($Last_Self->{coverage_filename}, qr/ToggleLogIf.*,c=>9\);/); + +ok(1); +1; diff --git a/test_regress/t/t_psl_basic_off.pl b/test_regress/t/t_psl_basic_off.pl new file mode 100755 index 000000000..a48cbdeb2 --- /dev/null +++ b/test_regress/t/t_psl_basic_off.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +top_filename("t/t_psl_basic.v"); + +compile ( + v_flags2 => [], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_select_bad_msb.pl b/test_regress/t/t_select_bad_msb.pl new file mode 100755 index 000000000..b3fbe3e94 --- /dev/null +++ b/test_regress/t/t_select_bad_msb.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>1, + expect=> +'%Error: t/t_select_bad_msb.v:\d+: Unsupported: MSB < LSB of bit extract.* +%Error: Exiting due to.*', + ) if $Last_Self->{v3}; + +ok(1); +1; + diff --git a/test_regress/t/t_select_bad_msb.v b/test_regress/t/t_select_bad_msb.v new file mode 100644 index 000000000..07d74f74f --- /dev/null +++ b/test_regress/t/t_select_bad_msb.v @@ -0,0 +1,18 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003-2005 by Wilson Snyder. + +module t (clk); + input clk; + + reg [43:0] mi; + reg [3:0] sel2; + + always @ (posedge clk) begin + mi = 44'h123; + sel2 = mi[1:4]; + $write ("Bad select %x\n", sel2); + end +endmodule diff --git a/test_regress/t/t_select_bad_range.pl b/test_regress/t/t_select_bad_range.pl new file mode 100755 index 000000000..00782c15b --- /dev/null +++ b/test_regress/t/t_select_bad_range.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>$Last_Self->{v3}, + expect=> +'%Error: t/t_select_bad_range.v:\d+: Selection index out of range: 44:44 outside 43:0 +%Error: t/t_select_bad_range.v:\d+: Selection index out of range: 44:41 outside 43:0 +%Error: Exiting due to.*', + ); + +ok(1); +1; + diff --git a/test_regress/t/t_select_bad_range.v b/test_regress/t/t_select_bad_range.v new file mode 100644 index 000000000..af8478eb9 --- /dev/null +++ b/test_regress/t/t_select_bad_range.v @@ -0,0 +1,20 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (clk); + input clk; + + reg [43:0] mi; + reg sel; + reg [3:0] sel2; + + always @ (posedge clk) begin + mi = 44'h123; + sel = mi[44]; + sel2 = mi[44:41]; + $write ("Bad select %x %x\n", sel, sel2); + end +endmodule diff --git a/test_regress/t/t_select_index.pl b/test_regress/t/t_select_index.pl new file mode 100755 index 000000000..c8354c35b --- /dev/null +++ b/test_regress/t/t_select_index.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; + diff --git a/test_regress/t/t_select_index.v b/test_regress/t/t_select_index.v new file mode 100644 index 000000000..bb669378e --- /dev/null +++ b/test_regress/t/t_select_index.v @@ -0,0 +1,49 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003-2005 by Wilson Snyder. + +module t(/*AUTOARG*/ + // Inputs + clk + ); + + // surefire lint_off NBAJAM + + input clk; + reg [7:0] _ranit; + + reg [2:0] a; + reg [7:0] vvector; + reg [7:0] vvector_flip; + + // surefire lint_off STMINI + initial _ranit = 0; + + always @ (posedge clk) begin + a <= a + 3'd1; + vvector[a] <= 1'b1; // This should use "old" value for a + vvector_flip[~a] <= 1'b1; // This should use "old" value for a + // + //======== + if (_ranit==8'd0) begin + _ranit <= 8'd1; + $write("[%0t] t_select_index: Running\n", $time); + vvector <= 0; + vvector_flip <= 0; + a <= 3'b1; + end + else _ranit <= _ranit + 8'd1; + // + if (_ranit==8'd3) begin + $write("%x %x\n",vvector,vvector_flip); + if (vvector !== 8'b0000110) $stop; + if (vvector_flip !== 8'b0110_0000) $stop; + // + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_select_plus.pl b/test_regress/t/t_select_plus.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_select_plus.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_select_plus.v b/test_regress/t/t_select_plus.v new file mode 100644 index 000000000..caec81a87 --- /dev/null +++ b/test_regress/t/t_select_plus.v @@ -0,0 +1,77 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [83:4] from; + reg [83:4] to; + reg [6:0] bitn; + reg [3:0] nibblep; + reg [3:0] nibblem; + + reg [7:0] cyc; initial cyc=0; + + always @* begin + nibblep = from[bitn +: 4]; + nibblem = from[bitn -: 4]; + to = from; + to[bitn +: 4] = cyc[3:0]; + to[bitn -: 4] = cyc[3:0]; + end + + always @ (posedge clk) begin + //$write("[%0t] cyc==%d nibblep==%b nibblem==%b to^from==%x\n",$time, cyc, nibblep, nibblem, from^to); + cyc <= cyc + 8'd1; + case (cyc) + 8'd00: begin from<=80'h7bea9d779b67e48f67da; bitn<=7'd7; end + 8'd01: begin from<=80'hefddce326b11ca5dc448; bitn<=7'd8; end + 8'd02: begin from<=80'h3f99c5f34168401e210d; bitn<=7'd4; end // truncate -: + 8'd03: begin from<=80'hc90635f0a7757614ce3f; bitn<=7'd79; end + 8'd04: begin from<=80'hc761feca3820331370ec; bitn<=7'd83; end // truncate +: + 8'd05: begin from<=80'hd6e36077bf28244f84b5; bitn<=7'd6; end // half trunc + 8'd06: begin from<=80'h90118c5d3d285a1f3252; bitn<=7'd81; end // half trunc + 8'd07: begin from<=80'h38305da3d46b5859fe16; bitn<=7'd67; end + 8'd08: begin from<=80'h4b9ade23a8f5cc5b3111; bitn<=7'd127; end // truncate + 8'd09: begin + $write("*-* All Finished *-*\n"); + $finish; + end + default: ; + endcase + case (cyc) + 8'd00: ; + 8'd01: begin if ((nibblep & 4'b1111)!==4'b1011) $stop; if ((nibblem & 4'b1111)!==4'b1010) $stop; end + 8'd02: begin if ((nibblep & 4'b1111)!==4'b0100) $stop; if ((nibblem & 4'b1111)!==4'b0100) $stop; end + 8'd03: begin if ((nibblep & 4'b1111)!==4'b1101) $stop; if ((nibblem & 4'b0000)!==4'b0000) $stop; end + 8'd04: begin if ((nibblep & 4'b1111)!==4'b1001) $stop; if ((nibblem & 4'b1111)!==4'b1001) $stop; end + 8'd05: begin if ((nibblep & 4'b0000)!==4'b0000) $stop; if ((nibblem & 4'b1111)!==4'b1100) $stop; end + 8'd06: begin if ((nibblep & 4'b1111)!==4'b1101) $stop; if ((nibblem & 4'b0000)!==4'b0000) $stop; end + 8'd07: begin if ((nibblep & 4'b0000)!==4'b0000) $stop; if ((nibblem & 4'b1111)!==4'b0100) $stop; end + 8'd08: begin if ((nibblep & 4'b1111)!==4'b0000) $stop; if ((nibblem & 4'b1111)!==4'b0101) $stop; end + 8'd09: begin if ((nibblep & 4'b0000)!==4'b0000) $stop; if ((nibblem & 4'b0000)!==4'b0000) $stop; end + default: $stop; + endcase + case (cyc) + 8'd00: ; + 8'd01: begin if ((to^from)!==80'h0000000000000000005b) $stop; end + 8'd02: begin if ((to^from)!==80'h0000000000000000006c) $stop; end + 8'd03: begin if ((to^from)!==80'h0000000000000000000e) $stop; end + 8'd04: begin if ((to^from)!==80'h6d000000000000000000) $stop; end + 8'd05: begin if (((to^from)&~80'hf)!==80'h90000000000000000000) $stop; end // Exceed bounds, verilator may write index 0 + 8'd06: begin if (((to^from)&~80'hf)!==80'h00000000000000000020) $stop; end // Exceed bounds, verilator may write index 0 + 8'd07: begin if (((to^from)&~80'hf)!==80'h0c000000000000000000) $stop; end + 8'd08: begin if ((to^from)!==80'h0004d000000000000000) $stop; end + 8'd09: begin if (((to^from)&~80'hf)!==80'h00000000000000000000) $stop; end + default: $stop; + endcase + end + +endmodule diff --git a/test_regress/t/t_select_plusloop.pl b/test_regress/t/t_select_plusloop.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_select_plusloop.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_select_plusloop.v b/test_regress/t/t_select_plusloop.v new file mode 100644 index 000000000..1db5f1022 --- /dev/null +++ b/test_regress/t/t_select_plusloop.v @@ -0,0 +1,67 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg [31:0] narrow; + reg [63:0] quad; + reg [127:0] wide; + + integer cyc; initial cyc=0; + reg [7:0] crc; + reg [6:0] index; + + always @ (posedge clk) begin + //$write("[%0t] cyc==%0d crc=%b n=%x\n",$time, cyc, crc, narrow); + cyc <= cyc + 1; + if (cyc==0) begin + // Setup + narrow <= 32'h0; + quad <= 64'h0; + wide <= 128'h0; + crc <= 8'hed; + index <= 7'h0; + end + else if (cyc<90) begin + index <= index + 7'h2; + crc <= {crc[6:0], ~^ {crc[7],crc[5],crc[4],crc[3]}}; + // verilator lint_off WIDTH + if (index < 9'd20) narrow[index +: 3] <= crc[2:0]; + if (index < 9'd60) quad [index +: 3] <= crc[2:0]; + if (index < 9'd120) wide [index +: 3] <= crc[2:0]; + // + narrow[index[3:0]] <= ~narrow[index[3:0]]; + quad [~index[3:0]]<= ~quad [~index[3:0]]; + wide [~index[3:0]] <= ~wide [~index[3:0]]; + // verilator lint_on WIDTH + end + else if (cyc==90) begin + wide[12 +: 4] <=4'h6; quad[12 +: 4] <=4'h6; narrow[12 +: 4] <=4'h6; + wide[42 +: 4] <=4'h6; quad[42 +: 4] <=4'h6; + wide[82 +: 4] <=4'h6; + end + else if (cyc==91) begin + wide[0] <=1'b1; quad[0] <=1'b1; narrow[0] <=1'b1; + wide[41] <=1'b1; quad[41] <=1'b1; + wide[81] <=1'b1; + end + else if (cyc==99) begin + $write("[%0t] cyc==%0d crc=%b n=%x q=%x w=%x\n",$time, cyc, crc, narrow, quad, wide); + if (crc != 8'b01111001) $stop; + if (narrow != 32'h001661c7) $stop; + if (quad != 64'h16d49b6f64266039) $stop; + if (wide != 128'h012fd26d265b266ff6d49b6f64266039) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_select_runtime_range.pl b/test_regress/t/t_select_runtime_range.pl new file mode 100755 index 000000000..c8354c35b --- /dev/null +++ b/test_regress/t/t_select_runtime_range.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; + diff --git a/test_regress/t/t_select_runtime_range.v b/test_regress/t/t_select_runtime_range.v new file mode 100644 index 000000000..95c559412 --- /dev/null +++ b/test_regress/t/t_select_runtime_range.v @@ -0,0 +1,42 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (clk); + input clk; + + reg [43:0] mi; + reg [5:0] index; + reg read; + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + mi = 44'h123; + end + if (cyc==2) begin + index = 6'd43; + end + if (cyc==3) begin + read = mi[index]; + if (read!==1'b0) $stop; + end + if (cyc==4) begin + index = 6'd44; + end + if (cyc==5) begin + read = mi[index]; + $display("-Illegal read value: %x",read); + //if (read!==1'b1 && read!==1'bx) $stop; + end + if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_select_set.pl b/test_regress/t/t_select_set.pl new file mode 100755 index 000000000..c8354c35b --- /dev/null +++ b/test_regress/t/t_select_set.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; + diff --git a/test_regress/t/t_select_set.v b/test_regress/t/t_select_set.v new file mode 100644 index 000000000..70adf4bcb --- /dev/null +++ b/test_regress/t/t_select_set.v @@ -0,0 +1,48 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (clk); + input clk; + + reg [63:0] inwide; + reg [39:0] addr; + + integer cyc; initial cyc=1; + always @ (posedge clk) begin + $write ("%x %x\n", cyc, addr); + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + addr <= 40'h12_3456_7890; + end + if (cyc==2) begin + if (addr !== 40'h1234567890) $stop; + addr[31:0] <= 32'habcd_efaa; + end + if (cyc==3) begin + if (addr !== 40'h12abcdefaa) $stop; + addr[39:32] <= 8'h44; + inwide <= 64'hffeeddcc_11334466; + end + if (cyc==4) begin + if (addr !== 40'h44abcdefaa) $stop; + addr[31:0] <= inwide[31:0]; + end + if (cyc==5) begin + if (addr !== 40'h4411334466) $stop; + $display ("Flip [%x]\n", inwide[3:0]); + addr[{2'b0,inwide[3:0]}] <= ! addr[{2'b0,inwide[3:0]}]; + end + if (cyc==6) begin + if (addr !== 40'h4411334426) $stop; + end + if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule diff --git a/test_regress/t/t_trace_ena.pl b/test_regress/t/t_trace_ena.pl new file mode 100755 index 000000000..ee7c62aa1 --- /dev/null +++ b/test_regress/t/t_trace_ena.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2005 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. + +compile ( + v_flags2 => [$Last_Self->{v3}?'-trace':''], + ); + +execute ( + check_finished=>1, + ); + +if ($Last_Self->{v3}) { + file_grep ("obj_dir/Vt_trace_ena__Trace__Slow.cpp", qr/c_trace_on\"/x); + file_grep_not ("obj_dir/Vt_trace_ena__Trace__Slow.cpp", qr/_trace_off\"/x); +} + +ok(1); +1; diff --git a/test_regress/t/t_trace_ena.v b/test_regress/t/t_trace_ena.v new file mode 100644 index 000000000..a1f5a2d22 --- /dev/null +++ b/test_regress/t/t_trace_ena.v @@ -0,0 +1,35 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; initial cyc=1; + // verilator tracing_off + integer b_trace_off; + // verilator tracing_on + integer c_trace_on; + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + b_trace_off <= cyc; + c_trace_on <= b_trace_off; + if (cyc==4) begin + if (c_trace_on != 2) $stop; + end + if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_unroll_signed.pl b/test_regress/t/t_unroll_signed.pl new file mode 100755 index 000000000..7e6b144bc --- /dev/null +++ b/test_regress/t/t_unroll_signed.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Revision: 1.1 $$Date$$Author$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_unroll_signed.v b/test_regress/t/t_unroll_signed.v new file mode 100644 index 000000000..9f3a379b9 --- /dev/null +++ b/test_regress/t/t_unroll_signed.v @@ -0,0 +1,118 @@ +// $Revision: 1.1 $$Date$$Author$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + // Check look unroller + reg signed signed_tests_only = 1'sb1; + integer total; + + integer i; + reg [31:0] iu; + reg [31:0] dly_to_insure_was_unrolled [1:0]; + reg [2:0] i3; + + integer cyc; initial cyc=0; + always @ (posedge clk) begin + cyc <= cyc + 1; + case (cyc) + 1: begin + // >= signed + total = 0; + for (i=5; i>=0; i=i-1) begin + total = total - i -1; + dly_to_insure_was_unrolled[i] <= i; + end + if (total != -21) $stop; + end + 2: begin + // > signed + total = 0; + for (i=5; i>0; i=i-1) begin + total = total - i -1; + dly_to_insure_was_unrolled[i] <= i; + end + if (total != -20) $stop; + end + 3: begin + // < signed + total = 0; + for (i=1; i<5; i=i+1) begin + total = total - i -1; + dly_to_insure_was_unrolled[i] <= i; + end + if (total != -14) $stop; + end + 4: begin + // <= signed + total = 0; + for (i=1; i<=5; i=i+1) begin + total = total - i -1; + dly_to_insure_was_unrolled[i] <= i; + end + if (total != -20) $stop; + end + // UNSIGNED + 5: begin + // >= unsigned + total = 0; + for (iu=5; iu>=1; iu=iu-1) begin + total = total - iu -1; + dly_to_insure_was_unrolled[iu] <= iu; + end + if (total != -20) $stop; + end + 6: begin + // > unsigned + total = 0; + for (iu=5; iu>1; iu=iu-1) begin + total = total - iu -1; + dly_to_insure_was_unrolled[iu] <= iu; + end + if (total != -18) $stop; + end + 7: begin + // < unsigned + total = 0; + for (iu=1; iu<5; iu=iu+1) begin + total = total - iu -1; + dly_to_insure_was_unrolled[iu] <= iu; + end + if (total != -14) $stop; + end + 8: begin + // <= unsigned + total = 0; + for (iu=1; iu<=5; iu=iu+1) begin + total = total - iu -1; + dly_to_insure_was_unrolled[iu] <= iu; + end + if (total != -20) $stop; + end + //=== + 9: begin + // mostly cover a small index + total = 0; + for (i3=3'd0; i3<3'd7; i3=i3+3'd1) begin + total = total - {29'd0,i3} -1; + dly_to_insure_was_unrolled[i3[0]] <= 0; + end + if (total != -28) $stop; + end + //=== + 19: begin + $write("*-* All Finished *-*\n"); + $finish; + end + default: ; + endcase + end +endmodule diff --git a/test_regress/t/t_var_bad_hide.pl b/test_regress/t/t_var_bad_hide.pl new file mode 100755 index 000000000..8c07ed5ae --- /dev/null +++ b/test_regress/t/t_var_bad_hide.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>$Last_Self->{v3}, + expect=> +'%Warning-VARHIDDEN: t/t_var_bad_hide.v:\d+: Declaration of signal hides declaration in upper scope: top +.* +%Warning-VARHIDDEN: t/t_var_bad_hide.v:\d+: ... Location of original declaration +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_bad_hide.v b/test_regress/t/t_var_bad_hide.v new file mode 100644 index 000000000..4566a0169 --- /dev/null +++ b/test_regress/t/t_var_bad_hide.v @@ -0,0 +1,26 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t; + + // Check that the lint_on is obeyed. + // verilator lint_off VARHIDDEN + // verilator lint_on VARHIDDEN + + integer top; + + task x; + output top; + begin end + endtask + + initial begin + begin: lower + integer top; + end + end + +endmodule diff --git a/test_regress/t/t_var_bad_rsvd.pl b/test_regress/t/t_var_bad_rsvd.pl new file mode 100755 index 000000000..0e2951cae --- /dev/null +++ b/test_regress/t/t_var_bad_rsvd.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>$Last_Self->{v3}, + expect=> +'%Error: t/t_var_bad_rsvd.v:\d+: Unsupported: C\+\+ common word: bool +%Error: t/t_var_bad_rsvd.v:\d+: Unsupported: C\+\+ reserved word: switch +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_bad_rsvd.v b/test_regress/t/t_var_bad_rsvd.v new file mode 100644 index 000000000..b85f0ad13 --- /dev/null +++ b/test_regress/t/t_var_bad_rsvd.v @@ -0,0 +1,19 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + bool + ); + + input bool; // BAD + + reg vector; // OK, as not public + reg switch /*verilator public*/; // Bad + + initial $stop; + +endmodule diff --git a/test_regress/t/t_var_bad_sameas.pl b/test_regress/t/t_var_bad_sameas.pl new file mode 100755 index 000000000..b0d9e72f0 --- /dev/null +++ b/test_regress/t/t_var_bad_sameas.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + fails=>1, + expect=> +'%Error: t/t_var_bad_sameas.v:\d+: Unsupported in C: Cell has same name as variable: varfirst +%Error: t/t_var_bad_sameas.v:\d+: Unsupported in C: Task/function has same name as variable: varfirst +%Error: t/t_var_bad_sameas.v:\d+: Unsupported in C: Variable has same name as cell: cellfirst +%Error: t/t_var_bad_sameas.v:\d+: Unsupported in C: Task/function has same name as cell: cellfirst +%Error: t/t_var_bad_sameas.v:\d+: Unsupported in C: Variable has same name as task: taskfirst +%Error: t/t_var_bad_sameas.v:\d+: Unsupported in C: Cell has same name as task: taskfirst +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_bad_sameas.v b/test_regress/t/t_var_bad_sameas.v new file mode 100644 index 000000000..60f6ac1e9 --- /dev/null +++ b/test_regress/t/t_var_bad_sameas.v @@ -0,0 +1,24 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t; + + integer varfirst; + sub varfirst (); // Error: Cell hits var + task varfirst; begin end endtask // Error: Task hits var + + sub cellfirst (); + integer cellfirst; // Error: Var hits cell + task cellfirst; begin end endtask // Error: Task hits cell + + task taskfirst; begin end endtask + integer taskfirst; // Error: Var hits task + sub taskfirst (); // Error: Cell hits task + +endmodule + +module sub; +endmodule diff --git a/test_regress/t/t_var_dotted.v b/test_regress/t/t_var_dotted.v new file mode 100644 index 000000000..6213299d4 --- /dev/null +++ b/test_regress/t/t_var_dotted.v @@ -0,0 +1,164 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2006 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + // verilator lint_off MULTIDRIVEN + + wire [31:0] outb0c0; + wire [31:0] outb0c1; + wire [31:0] outb1c0; + wire [31:0] outb1c1; + + ma ma0 (.outb0c0(outb0c0), .outb0c1(outb0c1), + .outb1c0(outb1c0), .outb1c1(outb1c1) + ); + + global_mod #(32'hf00d) global_cell (); + global_mod #(32'hf22d) global_cell2 (); + + input clk; + integer cyc=1; + + always @ (posedge clk) begin + cyc <= cyc + 1; + //$write("[%0t] cyc%0d: %0x %0x %0x %0x\n", $time, cyc, outb0c0, outb0c1, outb1c0, outb1c1); + if (cyc==2) begin + if (global_cell.global != 32'hf00d) $stop; + if (global_cell2.global != 32'hf22d) $stop; + if (outb0c0 != 32'h00) $stop; + if (outb0c1 != 32'h01) $stop; + if (outb1c0 != 32'h10) $stop; + if (outb1c1 != 32'h11) $stop; + end + if (cyc==3) begin + // Can we scope down and read and write vars? + ma0.mb0.mc0.out <= ma0.mb0.mc0.out + 32'h100; + ma0.mb0.mc1.out <= ma0.mb0.mc1.out + 32'h100; + ma0.mb1.mc0.out <= ma0.mb1.mc0.out + 32'h100; + ma0.mb1.mc1.out <= ma0.mb1.mc1.out + 32'h100; + end + if (cyc==4) begin + if (outb0c0 != 32'h100) $stop; + if (outb0c1 != 32'h101) $stop; + if (outb1c0 != 32'h110) $stop; + if (outb1c1 != 32'h111) $stop; + end + if (cyc==5) begin + if (outb0c0 != 32'h1100) $stop; + if (outb0c1 != 32'h2101) $stop; + if (outb1c0 != 32'h2110) $stop; + if (outb1c1 != 32'h3111) $stop; + end + if (cyc==6) begin + if (outb0c0 != 32'h31100) $stop; + if (outb0c1 != 32'h02101) $stop; + if (outb1c0 != 32'h42110) $stop; + if (outb1c1 != 32'h03111) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +`ifdef USE_INLINE_MID + `define INLINE_MODULE /*verilator inline_module*/ + `define INLINE_MID_MODULE /*verilator no_inline_module*/ +`else + `ifdef USE_INLINE + `define INLINE_MODULE /*verilator inline_module*/ + `define INLINE_MID_MODULE /*verilator inline_module*/ + `else + `define INLINE_MODULE /*verilator public_module*/ + `define INLINE_MID_MODULE /*verilator public_module*/ + `endif +`endif + +module global_mod; + `INLINE_MODULE + parameter INITVAL = 0; + integer global; + initial global = INITVAL; +endmodule + +module ma ( + output wire [31:0] outb0c0, + output wire [31:0] outb0c1, + output wire [31:0] outb1c0, + output wire [31:0] outb1c1 + ); + `INLINE_MODULE + mb #(0) mb0 (.outc0(outb0c0), .outc1(outb0c1)); + mb #(1) mb1 (.outc0(outb1c0), .outc1(outb1c1)); +endmodule + +module mb ( + output wire [31:0] outc0, + output wire [31:0] outc1 + ); + `INLINE_MID_MODULE + parameter P2 = 0; + mc #(P2,0) mc0 (.out(outc0)); + mc #(P2,1) mc1 (.out(outc1)); + global_mod #(32'hf33d) global_cell2 (); + + wire reach_up_clk = t.clk; + always @(reach_up_clk) begin + if (P2==0) begin // Only for mb0 + if (outc0 !== t.ma0.mb0.mc0.out) $stop; // Top module name and lower instances + if (outc0 !== ma0.mb0.mc0.out) $stop; // Upper module name and lower instances + if (outc0 !== ma .mb0.mc0.out) $stop; // Upper module name and lower instances + if (outc0 !== mb.mc0.out) $stop; // This module name and lower instances + if (outc0 !== mb0.mc0.out) $stop; // Upper instance name and lower instances + if (outc0 !== mc0.out) $stop; // Lower instances + + if (outc1 !== t.ma0.mb0.mc1.out) $stop; // Top module name and lower instances + if (outc1 !== ma0.mb0.mc1.out) $stop; // Upper module name and lower instances + if (outc1 !== ma .mb0.mc1.out) $stop; // Upper module name and lower instances + if (outc1 !== mb.mc1.out) $stop; // This module name and lower instances + if (outc1 !== mb0.mc1.out) $stop; // Upper instance name and lower instances + if (outc1 !== mc1.out) $stop; // Lower instances + end + end +endmodule + +module mc (output reg [31:0] out); + `INLINE_MODULE + parameter P2 = 0; + parameter P3 = 0; + initial begin + out = {24'h0,P2[3:0],P3[3:0]}; + //$write("%m P2=%0x p3=%0x out=%x\n",P2, P3, out); + end + + // Can we look from the top module name down? + wire reach_up_clk = t.clk; + wire [31:0] reach_up_cyc = t.cyc; + + always @ (posedge reach_up_clk) begin + //$write("[%0t] %m: Got reachup, cyc=%0d\n", $time, reach_up_cyc); + if (reach_up_cyc==2) begin + if (global_cell.global != 32'hf00d) $stop; + if (global_cell2.global != 32'hf33d) $stop; + end + if (reach_up_cyc==4) begin + out[15:12] <= {P2[3:0]+P3[3:0]+4'd1}; + end + if (reach_up_cyc==5) begin + // Can we set another instance? + if (P3==1) begin // Without this, there are two possible correct answers... + mc0.out[19:16] <= {mc0.out[19:16]+P2[3:0]+P3[3:0]+4'd2}; + $display("%m Set %x->%x %x %x %x %x",mc0.out, {mc0.out[19:16]+P2[3:0]+P3[3:0]+4'd2}, mc0.out[19:16],P2[3:0],P3[3:0],4'd2); + end + end + end +endmodule diff --git a/test_regress/t/t_var_dotted_inl0.pl b/test_regress/t/t_var_dotted_inl0.pl new file mode 100755 index 000000000..60c50dce1 --- /dev/null +++ b/test_regress/t/t_var_dotted_inl0.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +top_filename("t/t_var_dotted.v"); + +compile ( + v_flags2 => ['+define+NOUSE_INLINE',], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_dotted_inl1.pl b/test_regress/t/t_var_dotted_inl1.pl new file mode 100755 index 000000000..9ab23952e --- /dev/null +++ b/test_regress/t/t_var_dotted_inl1.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +top_filename("t/t_var_dotted.v"); + +compile ( + v_flags2 => ['+define+USE_INLINE',], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_dotted_inl2.pl b/test_regress/t/t_var_dotted_inl2.pl new file mode 100755 index 000000000..705d1ed99 --- /dev/null +++ b/test_regress/t/t_var_dotted_inl2.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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. + +top_filename("t/t_var_dotted.v"); + +compile ( + v_flags2 => ['+define+USE_INLINE_MID',], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_in_assign_bad.pl b/test_regress/t/t_var_in_assign_bad.pl new file mode 100755 index 000000000..aa1fab17c --- /dev/null +++ b/test_regress/t/t_var_in_assign_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2005 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. + +compile ( + fails=>1, + expect=> +'%Error: t/t_var_in_assign_bad.v:\d+: Assigning to input variable: value +%Error: t/t_var_in_assign_bad.v:\d+: Assigning to input variable: valueSub +%Error: Exiting due to.*', + ) if $Last_Self->{v3}; + +ok(1); +1; diff --git a/test_regress/t/t_var_in_assign_bad.v b/test_regress/t/t_var_in_assign_bad.v new file mode 100644 index 000000000..28659543e --- /dev/null +++ b/test_regress/t/t_var_in_assign_bad.v @@ -0,0 +1,22 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2005 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + value + ); + input [3:0] value; + assign value = 4'h0; + sub sub (.valueSub (value[3:0])); +endmodule + +module sub (/*AUTOARG*/ + // Inputs + valueSub + ); + input [3:0] valueSub; + assign valueSub = 4'h0; +endmodule diff --git a/test_regress/t/t_var_init.pl b/test_regress/t/t_var_init.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_var_init.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_init.v b/test_regress/t/t_var_init.v new file mode 100644 index 000000000..5bcdc29c1 --- /dev/null +++ b/test_regress/t/t_var_init.v @@ -0,0 +1,32 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + parameter [31:0] p2=2, p3=3; + integer i2=2, i3=3; + reg [31:0] r2=2, r3=3; + wire [31:0] w2=2, w3=3; + + always @ (posedge clk) begin + if (p2 !== 2) $stop; + if (p3 !== 3) $stop; + if (i2 !== 2) $stop; + if (i3 !== 3) $stop; + if (r2 !== 2) $stop; + if (r3 !== 3) $stop; + if (w2 !== 2) $stop; + if (w3 !== 3) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_var_life.pl b/test_regress/t/t_var_life.pl new file mode 100755 index 000000000..7d5b675b5 --- /dev/null +++ b/test_regress/t/t_var_life.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + v_flags2 => ["--stats"], + ); + +if ($Last_Self->{v3}) { + file_grep ($Last_Self->{stats}, qr/Optimizations, Lifetime assign deletions\s+3/i); +} + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_life.v b/test_regress/t/t_var_life.v new file mode 100644 index 000000000..e5cd45f10 --- /dev/null +++ b/test_regress/t/t_var_life.v @@ -0,0 +1,109 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + // Life analysis checks + reg [15:0] life; + + // Ding case + reg [7:0] din; + reg [15:0] fixin; + always @* begin + fixin = {din[7:0],din[7:0]}; + case (din[1:0]) + 2'b00: begin + fixin = {fixin[14:0], 1'b1}; + if (cyc==101) $display("Prevent ?: optimization a"); + end + 2'b01: begin + fixin = {fixin[13:0], 2'b11}; + if (cyc==101) $display("Prevent ?: optimization b"); + end + 2'b10: begin + fixin = {fixin[12:0], 3'b111}; + if (cyc==101) $display("Prevent ?: optimization c"); + end + 2'b11: begin + fixin = {fixin[11:0], 4'b1111}; + if (cyc==101) $display("Prevent ?: optimization d"); + end + endcase + end + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc<=cyc+1; + if (cyc==1) begin + life = 16'h8000; // Dropped + life = 16'h0010; // Used below + if (life != 16'h0010) $stop; + // + life = 16'h0020; // Used below + if ($time < 10000) + if (life != 16'h0020) $stop; + // + life = 16'h8000; // Dropped + if ($time > 100000) begin + if ($time != 0) $stop; // Prevent conversion to ?: + life = 16'h1030; + end + else + life = 16'h0030; + if (life != 16'h0030) $stop; + // + life = 16'h0040; // Not dropped, no else below + if ($time > 100000) + life = 16'h1040; + if (life != 16'h0040) $stop; + // + life = 16'h8000; // Dropped + if ($time > 100000) begin + life = 16'h1050; + if (life != 0) $stop; // Ignored, as set is first + end + else begin + if ($time > 100010) + life = 16'h1050; + else life = 16'h0050; + end + if (life != 16'h0050) $stop; + end + if (cyc==2) begin + din <= 8'haa; + end + if (cyc==3) begin + din <= 8'hfb; + if (fixin != 16'h5557) $stop; + end + if (cyc==4) begin + din <= 8'h5c; + if (fixin != 16'hbfbf) $stop; + end + if (cyc==5) begin + din <= 8'hed; + if (fixin != 16'hb8b9) $stop; + end + if (cyc==6) begin + if (fixin != 16'hb7b7) $stop; + end + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_regress/t/t_var_local.pl b/test_regress/t/t_var_local.pl new file mode 100755 index 000000000..9a7e1014a --- /dev/null +++ b/test_regress/t/t_var_local.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_local.v b/test_regress/t/t_var_local.v new file mode 100644 index 000000000..d0bf2c53a --- /dev/null +++ b/test_regress/t/t_var_local.v @@ -0,0 +1,38 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t; + + integer top; + + initial begin + begin : a + integer lower; + lower = 1; + top = 1; + if (lower != 1) $stop; + begin : aa + integer lev2; + lev2 = 1; + lower = 2; + top = 2; + end + if (lower != 2) $stop; + end + begin : b + integer lower; + lower = 1; + top = 2; + begin : empty + begin : empty + end + end + end + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_var_outoforder.pl b/test_regress/t/t_var_outoforder.pl new file mode 100755 index 000000000..eff491d7a --- /dev/null +++ b/test_regress/t/t_var_outoforder.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_var_outoforder.v b/test_regress/t/t_var_outoforder.v new file mode 100644 index 000000000..4d6d13649 --- /dev/null +++ b/test_regress/t/t_var_outoforder.v @@ -0,0 +1,79 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2004 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + reg [125:0] a; + wire q; + + sub sub ( + .q (q), + .a (a), + .clk (clk)); + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + a <= 126'b1000; + end + if (cyc==2) begin + a <= 126'h1001; + end + if (cyc==3) begin + a <= 126'h1010; + end + if (cyc==4) begin + a <= 126'h1111; + if (q !== 1'b0) $stop; + end + if (cyc==5) begin + if (q !== 1'b1) $stop; + end + if (cyc==6) begin + if (q !== 1'b0) $stop; + end + if (cyc==7) begin + if (q !== 1'b0) $stop; + end + if (cyc==8) begin + if (q !== 1'b0) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +module sub ( + input clk, + input [125:0] a, + output reg q + ); + + // verilator public_module + + reg [125:0] g_r; + + wire [127:0] g_extend = { g_r, 1'b1, 1'b0 }; + + reg [6:0] sel; + wire g_sel = g_extend[sel]; + + always @ (posedge clk) begin + g_r <= a; + sel <= a[6:0]; + q <= g_sel; + end + +endmodule diff --git a/test_regress/t/t_var_pins_cc.pl b/test_regress/t/t_var_pins_cc.pl new file mode 100755 index 000000000..d4abf965f --- /dev/null +++ b/test_regress/t/t_var_pins_cc.pl @@ -0,0 +1,34 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +top_filename("t/t_var_pinsizes.v"); + +compile ( + v_flags2 => ['-cc'], + verilator_make_gcc => 0, + ) if $Last_Self->{v3}; + +if ($Last_Self->{v3}) { + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_IN8 \(i1,0,0\);/x); + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_IN8 \(i8,7,0\);/x); + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_IN16 \(i16,15,0\);/x); + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_IN \(i32,31,0\);/x); + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_IN64 \(i64,63,0\);/x); + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_INW \(i65,64,0,3\);/x); + + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_OUT8 \(o1,0,0\);/x); + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_OUT8 \(o8,7,0\);/x); + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_OUT16\(o16,15,0\);/x); + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_OUT \(o32,31,0\);/x); + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_OUT64\(o64,63,0\);/x); + file_grep ("obj_dir/Vt_var_pins_cc.h", qr/VL_OUTW \(o65,64,0,3\);/x); +} + +ok(1); +1; diff --git a/test_regress/t/t_var_pins_sc32.pl b/test_regress/t/t_var_pins_sc32.pl new file mode 100755 index 000000000..56ad3c51d --- /dev/null +++ b/test_regress/t/t_var_pins_sc32.pl @@ -0,0 +1,34 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +top_filename("t/t_var_pinsizes.v"); + +compile ( + v_flags2 => ['-sp -no-pins64'], + verilator_make_gcc => 0, + ) if $Last_Self->{v3}; + +if ($Last_Self->{v3}) { + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_in \s+ i1;/x); + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_in \s+ i8;/x); + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_in \s+ i16;/x); + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_in \s+ i32;/x); + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_in\s> \s+ i64;/x); + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_in\s> \s+ i65;/x); + + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_out \s+ o1;/x); + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_out \s+ o8;/x); + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_out \s+ o16;/x); + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_out \s+ o32;/x); + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_out\s> \s+ o64;/x); + file_grep ("obj_dir/Vt_var_pins_sc32.sp", qr/sc_out\s> \s+ o65;/x); +} + +ok(1); +1; diff --git a/test_regress/t/t_var_pins_sc64.pl b/test_regress/t/t_var_pins_sc64.pl new file mode 100755 index 000000000..d7092a59b --- /dev/null +++ b/test_regress/t/t_var_pins_sc64.pl @@ -0,0 +1,34 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id:$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +top_filename("t/t_var_pinsizes.v"); + +compile ( + v_flags2 => ['-sp -pins64'], + verilator_make_gcc => 0, + ) if $Last_Self->{v3}; + +if ($Last_Self->{v3}) { + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_in \s+ i1;/x); + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_in \s+ i8;/x); + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_in \s+ i16;/x); + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_in \s+ i32;/x); + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_in \s+ i64;/x); + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_in\s> \s+ i65;/x); + + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_out \s+ o1;/x); + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_out \s+ o8;/x); + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_out \s+ o16;/x); + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_out \s+ o32;/x); + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_out \s+ o64;/x); + file_grep ("obj_dir/Vt_var_pins_sc64.sp", qr/sc_out\s> \s+ o65;/x); +} + +ok(1); +1; diff --git a/test_regress/t/t_var_pinsizes.v b/test_regress/t/t_var_pinsizes.v new file mode 100644 index 000000000..bd81b462d --- /dev/null +++ b/test_regress/t/t_var_pinsizes.v @@ -0,0 +1,37 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + input i1; + input [7:0] i8; + input [15:0] i16; + input [31:0] i32; + input [63:0] i64; + input [64:0] i65; + + output o1; + output [7:0] o8; + output [15:0] o16; + output [31:0] o32; + output [63:0] o64; + output [64:0] o65; + + always @ (posedge clk) begin + o1 <= i1; + o8 <= i8; + o16 <= i16; + o32 <= i32; + o64 <= i64; + o65 <= i65; + end + +endmodule diff --git a/test_regress/t/vlint b/test_regress/t/vlint new file mode 100755 index 000000000..1ced22c17 --- /dev/null +++ b/test_regress/t/vlint @@ -0,0 +1,12 @@ +#!/bin/sh +# $Id:$ +# DESCRIPTION: Verilator: Invoke linting +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +$DIRPROJECT_PREFIX/bin/vlint --brief \ + +librescan +libext+.v -y . +incdir+../include \ + --filt=STMINI,_NETNM,CWCCXX,CSYBEQ,CSEBEQ,NBAJAM,ITENST,STMFOR \ + $* diff --git a/test_sc/.cvsignore b/test_sc/.cvsignore new file mode 100644 index 000000000..b491e81b1 --- /dev/null +++ b/test_sc/.cvsignore @@ -0,0 +1,8 @@ +*.old +*.dmp +*.log +*.csrc +*.vcd +obj_* +logs +project diff --git a/test_sc/Makefile b/test_sc/Makefile new file mode 100644 index 000000000..bbe5d26c4 --- /dev/null +++ b/test_sc/Makefile @@ -0,0 +1,60 @@ +# $Id$ */ +#***************************************************************************** +# +# DESCRIPTION: Verilator Example: Makefile for inside source directory +# +# This calls the object directory makefile. That allows the objects to +# be placed in the "current directory" which simplifies the Makefile. +# +# 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 must point to the root of the VERILATOR kit +VERILATOR_ROOT := $(shell pwd)/.. +export VERILATOR_ROOT +PWD := $(shell pwd) + +DEBUG_ON = --debug + +###################################################################### +default: precopy prep preproc compile run +debug: precopy prep_dbg preproc compile run +nopublic: precopy prep_dbg_np preproc compile run + +V_FLAGS = -f $(PWD)/../test_v/input.vc +VERILATOR_FLAGS = --sc $(V_FLAGS) top.v + +precopy: obj_dir obj_dir/sc_main.cpp +obj_dir/sc_main.cpp: ../test_sp/sc_main.cpp + cp $^ $@ + +prep: + perl $(VERILATOR_ROOT)/bin/verilator $(VERILATOR_FLAGS) +prep_dbg: + perl $(VERILATOR_ROOT)/bin/verilator $(DEBUG_ON) $(VERILATOR_FLAGS) +prep_dbg_np: + perl $(VERILATOR_ROOT)/bin/verilator $(DEBUG_ON) $(VERILATOR_FLAGS) --nopublic + +preproc: + cd obj_dir ; $(MAKE) -j 1 -f ../Makefile_obj preproc + +compile: + cd obj_dir ; $(MAKE) -j 3 -f ../Makefile_obj + +run: + obj_dir/simx + +###################################################################### + +obj_dir: + mkdir $@ + +###################################################################### + +maintainer-copy:: +clean mostlyclean distclean maintainer-clean:: + -rm -rf obj_dir *.log *.dmp *.vpd *.vcd core + diff --git a/test_sc/Makefile_obj b/test_sc/Makefile_obj new file mode 100644 index 000000000..050db89ff --- /dev/null +++ b/test_sc/Makefile_obj @@ -0,0 +1,39 @@ +# $Id$ -*- Makefile -*- +#***************************************************************************** +# +# DESCRIPTION: Verilator Example: Makefile for inside object directory +# +# This executed in the object directory, and called by ../Makefile +# +# 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. +# +#***************************************************************************** + +default: simx +include Vtop.mk + +####################################################################### +# Use sp_log.cpp, so we can get output in sim.log + +CPPFLAGS += -DUTIL_PRINTF=printf +CPPFLAGS += -Wno-deprecated +CPPFLAGS += $(SYSTEMC_CXX_FLAGS) + +LDFLAGS += $(SYSTEMC_CXX_FLAGS) + +####################################################################### +# Linking final exe -- presumes have a sim_main.cpp + +SP_SRCS = verilated.o + +SC_LIB = $(SYSTEMC)/lib-$(VM_SC_TARGET_ARCH)/libsystemc.a + +simx: sc_main.o $(SP_SRCS) \ + $(VM_PREFIX)__ALL.a $(SC_LIB) + $(CXX) $(LDFLAGS) -g $^ $(LOADLIBES) $(LDLIBS) -o $@ $(SC_LIBS) $(LIBS) 2>&1 | c++filt + +VPATH += ../test_sp + +sc_main.o: sc_main.cpp $(VM_PREFIX).h diff --git a/test_sp/.cvsignore b/test_sp/.cvsignore new file mode 100644 index 000000000..b491e81b1 --- /dev/null +++ b/test_sp/.cvsignore @@ -0,0 +1,8 @@ +*.old +*.dmp +*.log +*.csrc +*.vcd +obj_* +logs +project diff --git a/test_sp/Makefile b/test_sp/Makefile new file mode 100644 index 000000000..a68eb7709 --- /dev/null +++ b/test_sp/Makefile @@ -0,0 +1,59 @@ +# $Id$ */ +#***************************************************************************** +# +# DESCRIPTION: Verilator Example: Makefile for inside source directory +# +# This calls the object directory makefile. That allows the objects to +# be placed in the "current directory" which simplifies the Makefile. +# +# 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 must point to the root of the VERILATOR kit +VERILATOR_ROOT := $(shell pwd)/.. +export VERILATOR_ROOT +PWD := $(shell pwd) + +DEBUG_ON = --debug --trace-dups --output-split 100 + +###################################################################### +default: prep preproc compile run coverage +debug: prep_dbg preproc compile run coverage +nopublic: prep_dbg_np preproc compile run coverage + +V_FLAGS = -f $(PWD)/../test_v/input.vc +VERILATOR_FLAGS = --public --sp --coverage --stats --trace $(V_FLAGS) top.v + +prep: + perl $(VERILATOR_ROOT)/bin/verilator $(VERILATOR_FLAGS) +prep_dbg: + perl $(VERILATOR_ROOT)/bin/verilator $(DEBUG_ON) $(VERILATOR_FLAGS) +prep_dbg_np: + perl $(VERILATOR_ROOT)/bin/verilator $(DEBUG_ON) $(VERILATOR_FLAGS) --nopublic + +preproc: + cd obj_dir ; $(MAKE) -j 1 -f ../Makefile_obj preproc + +compile: + cd obj_dir ; $(MAKE) -j 3 -f ../Makefile_obj + +run: + obj_dir/simx + +coverage: + vcoverage $(V_FLAGS) + +###################################################################### + +obj_dir: + mkdir $@ + +###################################################################### + +maintainer-copy:: +clean mostlyclean distclean maintainer-clean:: + -rm -rf obj_dir *.log *.dmp *.vpd *.vcd core + diff --git a/test_sp/Makefile_obj b/test_sp/Makefile_obj new file mode 100644 index 000000000..8012e016c --- /dev/null +++ b/test_sp/Makefile_obj @@ -0,0 +1,37 @@ +# $Id$ -*- Makefile -*- +#***************************************************************************** +# +# DESCRIPTION: Verilator Example: Makefile for inside object directory +# +# This executed in the object directory, and called by ../Makefile +# +# 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. +# +#***************************************************************************** + +default: simx +include Vtop.mk + +####################################################################### +# Use sp_log.cpp, so we can get output in sim.log + +CPPFLAGS += -DUTIL_PRINTF=sp_log_printf +CPPFLAGS += -Wno-deprecated +CPPFLAGS += $(SYSTEMC_CXX_FLAGS) + +LDFLAGS += $(SYSTEMC_CXX_FLAGS) + +####################################################################### +# Linking final exe -- presumes have a sim_main.cpp + +SP_SRCS = Sp.o verilated.o + +SC_LIB = $(SYSTEMC)/lib-$(VM_SC_TARGET_ARCH)/libsystemc.a + +simx: sc_main.o $(SP_SRCS) \ + $(VM_PREFIX)__ALL.a $(SC_LIB) + $(CXX) $(LDFLAGS) -g $^ $(LOADLIBES) $(LDLIBS) -o $@ $(SC_LIBS) $(LIBS) 2>&1 | c++filt + +sc_main.o: sc_main.cpp $(VM_PREFIX).h diff --git a/test_sp/sc_main.cpp b/test_sp/sc_main.cpp new file mode 100644 index 000000000..cd25f2b83 --- /dev/null +++ b/test_sp/sc_main.cpp @@ -0,0 +1,159 @@ +// $Id$ -*- SystemC -*- +// DESCRIPTION: Verilator Example: Top level main for invoking SystemC model +// +// 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. +//==================================================================== + +#include +#include +#include +#include + +#ifdef SYSTEMPERL +# include "systemperl.h" // SystemC + SystemPerl global header +# include "sp_log.h" // Logging cout to files +# include "SpTraceVcd.h" +# include "SpCoverage.h" +#else +# include "systemc.h" // SystemC global header +#endif + +#include "Vtop.h" // Top level header, generated from verilog + +Vtop *top; + +int sc_main(int argc, char* argv[]) { + Verilated::randReset(2); + Verilated::debug(0); // We compiled with it on for testing, turn it back off + + // General logfile + ios::sync_with_stdio(); +#ifdef SYSTEMPERL + sp_log_file simlog ("sim.log"); + simlog.redirect_cout(); +#endif + + // Defaults +#if (SYSTEMC_VERSION>20011000) +#else + sc_time dut(1.0, sc_ns); + sc_set_default_time_unit(dut); +#endif + + //========== + // Define the Clocks + + cout << ("Defining Clocks\n"); + sc_clock clk ("clk",10, 0.5, 3, true); + sc_clock fastclk ("fastclk",2, 0.5, 2, true); + sc_signal reset_l; + sc_signal passed; + sc_signal in_small; + sc_signal > in_quad; + sc_signal > in_wide; + sc_signal out_small; + sc_signal > out_quad; + sc_signal > out_wide; + + //========== + // Part under test + +#ifdef SYSTEMPERL + SP_CELL (top, Vtop); + SP_PIN (top, clk, clk); + SP_PIN (top, fastclk, fastclk); + SP_PIN (top, reset_l, reset_l); + SP_PIN (top, passed, passed); + SP_PIN (top, in_small, in_small); + SP_PIN (top, in_quad, in_quad); + SP_PIN (top, in_wide, in_wide); + SP_PIN (top, out_small, out_small); + SP_PIN (top, out_quad, out_quad); + SP_PIN (top, out_wide, out_wide); +#else + Vtop* top = new Vtop("top"); + top->clk (clk); + top->fastclk (fastclk); + top->reset_l (reset_l); + top->passed (passed); + top->in_small (in_small); + top->in_quad (in_quad); + top->in_wide (in_wide); + top->out_small (out_small); + top->out_quad (out_quad); + top->out_wide (out_wide); +#endif + + //========== + // Waves + +#if WAVES + // Before any evaluation, need to know to calculate those signals only used for tracing + Verilated::traceEverOn(true); +#endif + + // You must do one evaluation before enabling waves, in order to allow + // SystemC to interconnect everything for testing. + cout <<("Test initialization...\n"); + reset_l = 1; + sc_start(1); + + //========== + // Waves + +#if WAVES + cout << "Enabling waves...\n"; + SpTraceFile* tfp = new SpTraceFile; + top->trace (tfp, 99); + tfp->open ("vl_dump.vcd"); +#endif + + //========== + // Start of Test + + cout <<("Test beginning...\n"); + + reset_l = 1; + while (VL_TIME_Q() < 60 && !passed) { +#if WAVES + // Flush the wave files each cycle so we can immediately see the output + // Don't do this in "real" programs, do it in a abort() handler instead + tfp->flush(); +#endif + if (VL_TIME_Q() > 10) { + reset_l = 1; // Deassert reset + } else if (VL_TIME_Q() > 1) { + reset_l = 0; // Assert reset + } + sc_start(1); + } + + top->final(); + + //========== + // Close Waves +#if WAVES + tfp->close(); +#endif + + if (!passed) { + UTIL_PRINTF ("A Test failed!!\n"); + abort(); + } + + //========== + // Coverage analysis (since test passed) + mkdir("logs", 0777); +#ifdef SYSTEMPERL + SpCoverage::write(); // Writes logs/coverage.pl +#endif + + //========== + // Close LogFiles + + cout << "*-* All Finished *-*\n"; // Magic if using perl's Log::Detect + + return(0); +} diff --git a/test_v/input.vc b/test_v/input.vc new file mode 100644 index 000000000..c9b8aba5b --- /dev/null +++ b/test_v/input.vc @@ -0,0 +1,7 @@ + ++librescan +libext+.v + -y ../test_v + +incdir+../test_v + +incdir+../include + + \ No newline at end of file diff --git a/test_v/t.v b/test_v/t.v new file mode 100644 index 000000000..b172e00fb --- /dev/null +++ b/test_v/t.v @@ -0,0 +1,121 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Outputs + passed, + // Inputs + clk, fastclk, reset_l + ); + + input clk /*verilator sc_clock*/; + input fastclk /*verilator sc_clock*/; + input reset_l; + output passed; + + // Combine passed signals from each sub signal + // verilator lint_off MULTIDRIVEN + wire [20:0] passedv; + // verilator lint_on MULTIDRIVEN + wire passed = &passedv; + + t_arith tarith + (.passed (passedv[0]), + /*AUTOINST*/ + // Inputs + .clk (clk)); + t_case tcase + (.passed (passedv[1]), + /*AUTOINST*/ + // Inputs + .clk (clk)); + assign passedv[2] = 1'b1; + t_equal tequal + (.passed (passedv[3]), + /*AUTOINST*/ + // Inputs + .clk (clk)); + assign passedv[4] = 1'b1; + t_initial tinitial + (.passed (passedv[5]), + /*AUTOINST*/ + // Inputs + .clk (clk)); + t_inst tinst + (.passed (passedv[6]), + /*AUTOINST*/ + // Inputs + .clk (clk), + .fastclk (fastclk)); + t_param tparam + (.passed (passedv[7]), + /*AUTOINST*/ + // Inputs + .clk (clk)); + t_rnd trnd + (.passed (passedv[8]), + /*AUTOINST*/ + // Inputs + .clk (clk)); + t_mem tmem + (.passed (passedv[9]), + /*AUTOINST*/ + // Inputs + .clk (clk)); + t_blocking tblocking + (.passed (passedv[10]), + /*AUTOINST*/ + // Inputs + .clk (clk), + .reset_l (reset_l)); + t_clk tclk + (.passed (passedv[11]), + /*AUTOINST*/ + // Inputs + .fastclk (fastclk), + .clk (clk), + .reset_l (reset_l)); + assign passedv[12] = 1'b1; + t_func tfunc + (.passed (passedv[13]), + /*AUTOINST*/ + // Inputs + .clk (clk)); + t_chg tchg + (.passed (passedv[14]), + /*AUTOINST*/ + // Inputs + .clk (clk), + .fastclk (fastclk)); + t_extend textend + (.passed (passedv[15]), + /*AUTOINST*/ + // Inputs + .clk (clk)); + t_loop tloop + (.passed (passedv[16]), + /*AUTOINST*/ + // Inputs + .clk (clk)); + assign passedv[17] = 1'b1; + assign passedv[18] = 1'b1; + t_task ttask + (.passed (passedv[19]), + /*AUTOINST*/ + // Inputs + .clk (clk)); + t_netlist tnetlist + (.passed (passedv[20]), + .also_fastclk (fastclk), + /*AUTOINST*/ + // Inputs + .fastclk (fastclk)); + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_v/t_arith.v b/test_v/t_arith.v new file mode 100644 index 000000000..3e5aaf237 --- /dev/null +++ b/test_v/t_arith.v @@ -0,0 +1,153 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t_arith(/*AUTOARG*/ + // Outputs + passed, + // Inputs + clk + ); + input clk; + output passed; reg passed; initial passed = 0; + reg _ranit; + + reg [2:0] xor3; + reg [1:0] xor2; + reg [0:0] xor1; + reg [2:0] ma, mb; + reg [9:0] mc; + reg [4:0] mr1; + reg [30:0] mr2; + + reg [67:0] sh1; + reg [67:0] shq; + + wire foo, bar; assign {foo,bar} = 2'b1_0; + + // surefire lint_off STMINI + initial _ranit = 0; + + wire [4:0] cond_check = (( xor2 == 2'b11) ? 5'h1 + : (xor2 == 2'b00) ? 5'h2 + : (xor2 == 2'b01) ? 5'h3 + : 5'h4); + + wire ctrue = 1'b1 ? cond_check[1] : cond_check[0]; + wire cfalse = 1'b0 ? cond_check[1] : cond_check[0]; + wire cif = cond_check[2] ? cond_check[1] : cond_check[0]; + wire cifn = (!cond_check[2]) ? cond_check[1] : cond_check[0]; + + wire [4:0] doubleconc = {1'b0, 1'b1, 1'b0, cond_check[0], 1'b1}; + + wire zero = 1'b0; + wire one = 1'b1; + wire [5:0] rep6 = {6{one}}; + + always @ (posedge clk) begin + if (!_ranit) begin + _ranit <= 1; + $write("[%0t] t_arith: Running\n",$time); + + if (rep6 != 6'b111111) $stop; + if (!one) $stop; + if (~one) $stop; + + if (( 1'b0 ? 3'h3 : 1'b0 ? 3'h2 : 1'b1 ? 3'h1 : 3'h0) !== 3'h1) $stop; + // verilator lint_off WIDTH + // surefire lint_off CNDWID + if (( 8'h10 + 1'b0 ? 8'he : 8'hf) !== 8'he) $stop; // + is higher then ? + // surefire lint_on CNDWID + // verilator lint_on WIDTH + + // surefire lint_off SEQASS + xor1 = 1'b1; + xor2 = 2'b11; + xor3 = 3'b111; + // verilator lint_off WIDTH + if (1'b1 & | (!xor3)) $stop; + // verilator lint_on WIDTH + if ({1{xor1}} != 1'b1) $stop; + if ({4{xor1}} != 4'b1111) $stop; + if (!(~xor1) !== ~(!xor1)) $stop; + if ((^xor1) !== 1'b1) $stop; + if ((^xor2) !== 1'b0) $stop; + if ((^xor3) !== 1'b1) $stop; + if (~(^xor2) !== 1'b1) $stop; + if (~(^xor3) !== 1'b0) $stop; + if ((^~xor1) !== 1'b0) $stop; + if ((^~xor2) !== 1'b1) $stop; + if ((^~xor3) !== 1'b0) $stop; + if ((~^xor1) !== 1'b0) $stop; + if ((~^xor2) !== 1'b1) $stop; + if ((~^xor3) !== 1'b0) $stop; + xor1 = 1'b0; + xor2 = 2'b10; + xor3 = 3'b101; + if ((^xor1) !== 1'b0) $stop; + if ((^xor2) !== 1'b1) $stop; + if ((^xor3) !== 1'b0) $stop; + if (~(^xor2) !== 1'b0) $stop; + if (~(^xor3) !== 1'b1) $stop; + if ((^~xor1) !== 1'b1) $stop; + if ((^~xor2) !== 1'b0) $stop; + if ((^~xor3) !== 1'b1) $stop; + if ((~^xor1) !== 1'b1) $stop; + if ((~^xor2) !== 1'b0) $stop; + if ((~^xor3) !== 1'b1) $stop; + + ma = 3'h3; + mb = 3'h4; + mc = 10'h5; + + // surefire lint_off ASWESB + // surefire lint_off ASWESS + // surefire lint_off ASWEMB + // surefire lint_off CWECBB + + mr1 = ma * mb; // Lint ASWESB: Assignment width mismatch + mr2 = 30'h5 * mc; // Lint ASWESB: Assignment width mismatch + if (mr1 !== 5'd12) $stop; + if (mr2 !== 31'd25) $stop; // Lint CWECBB: Comparison width mismatch + + sh1 = 68'hf_def1_9abc_5678_1234; + shq = sh1 >> 16; + if (shq !== 68'hf_def1_9abc_5678) $stop; + shq = sh1 << 16; // Lint ASWESB: Assignment width mismatch + if (shq !== 68'h1_9abc_5678_1234_0000) $stop; + + // surefire lint_on SEQASS + + // Test display extraction widthing + $display("[%0t] %x %x %x(%d)", $time, shq[2:0], shq[2:0]<<2, xor3[2:0], xor3[2:0]); + + $write("[%0t] t_arith: Passed\n",$time); + passed <= 1'b1; + end + end + + + reg [63:0] m_data_pipe2_r; + reg [31:0] m_corr_data_w0, m_corr_data_w1; + reg [7:0] m_corr_data_b8; + initial begin + m_data_pipe2_r = 64'h1234_5678_9abc_def0; + {m_corr_data_b8, m_corr_data_w1, m_corr_data_w0} = { m_data_pipe2_r[63:57], 1'b0, //m_corr_data_b8 [7:0] + m_data_pipe2_r[56:26], 1'b0, //m_corr_data_w1 [31:0] + m_data_pipe2_r[25:11], 1'b0, //m_corr_data_w0 [31:16] + m_data_pipe2_r[10:04], 1'b0, //m_corr_data_w0 [15:8] + m_data_pipe2_r[03:01], 1'b0, //m_corr_data_w0 [7:4] + m_data_pipe2_r[0], 3'b000 //m_corr_data_w0 [3:0] + }; + if (m_corr_data_w0 != 32'haf36de00) $stop; + if (m_corr_data_w1 != 32'h1a2b3c4c) $stop; + if (m_corr_data_b8 != 8'h12) $stop; + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_v/t_blocking.v b/test_v/t_blocking.v new file mode 100644 index 000000000..c42d6bc79 --- /dev/null +++ b/test_v/t_blocking.v @@ -0,0 +1,107 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t_blocking(/*AUTOARG*/ + // Outputs + passed, + // Inputs + clk, reset_l + ); + + input clk; + input reset_l; + output passed; reg passed; initial passed = 0; + + integer _mode; initial _mode=0; + reg [7:0] a; + reg [7:0] b; + reg [7:0] c; + + reg [7:0] mode_d1r; + reg [7:0] mode_d2r; + reg [7:0] mode_d3r; + + // surefire lint_off ITENST + // surefire lint_off STMINI + // surefire lint_off NBAJAM + + always @ (posedge clk or negedge reset_l) begin // filp-flops with asynchronous reset + if (!reset_l) begin + _mode <= 0; + passed <= 0; + end + else begin + _mode <= _mode + 1; + if (_mode==0) begin + $write("[%0t] t_blocking: Running\n", $time); + a <= 8'd0; + b <= 8'd0; + c <= 8'd0; + end + else if (_mode==1) begin + $write("[%0t] t_blocking: Running\n", $time); + if (a !== 8'd0) $stop; + if (b !== 8'd0) $stop; + if (c !== 8'd0) $stop; + a <= b; + b <= 8'd1; + c <= b; + if (a !== 8'd0) $stop; + if (b !== 8'd0) $stop; + if (c !== 8'd0) $stop; + end + else if (_mode==2) begin + $write("[%0t] t_blocking: Running\n", $time); + if (a !== 8'd0) $stop; + if (b !== 8'd1) $stop; + if (c !== 8'd0) $stop; + a <= b; + b <= 8'd2; + c <= b; + if (a !== 8'd0) $stop; + if (b !== 8'd1) $stop; + if (c !== 8'd0) $stop; + end + else if (_mode==3) begin + if (a !== 8'd1) $stop; + if (b !== 8'd2) $stop; + if (c !== 8'd1) $stop; + end + else if (_mode==4) begin + if (mode_d3r != 8'd1) $stop; + $write("[%0t] t_blocking: Passed\n", $time); + passed <= 1'b1; + end + end + end + + always @ (posedge clk) begin + mode_d3r <= mode_d2r; + mode_d2r <= mode_d1r; + mode_d1r <= _mode[7:0]; + end + + reg [14:10] bits; + // surefire lint_off SEQASS + always @ (posedge clk) begin + if (_mode==1) begin + bits[14:13] <= 2'b11; + bits[12] <= 1'b1; + end + if (_mode==2) begin + bits[11:10] <= 2'b10; + bits[13] <= 0; + end + if (_mode==3) begin + if (bits !== 5'b10110) $stop; + end + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_v/t_case.v b/test_v/t_case.v new file mode 100644 index 000000000..a6ae70ee7 --- /dev/null +++ b/test_v/t_case.v @@ -0,0 +1,202 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +`include "verilated.v" + +module t_case(/*AUTOARG*/ + // Outputs + passed, + // Inputs + clk + ); + + input clk; + output passed; reg passed; initial passed = 0; + reg _ranit; + + reg rnd; + reg [2:0] a; + reg [2:0] b; + reg [31:0] wide; + + // surefire lint_off STMINI + initial _ranit = 0; + + wire sigone1 = 1'b1; + wire sigone2 = 1'b1; + reg ok; + + parameter [1:0] twounkn = 2'b?; // This gets extended to 2'b?? + + // Large case statements should be well optimizable. + reg [2:0] anot; + always @ (/*AS*/a) begin + casez (a) + default: anot = 3'b001; + 3'd0: anot = 3'b111; + 3'd1: anot = 3'b110; + 3'd2: anot = 3'b101; + 3'd3: anot = 3'b101; + 3'd4: anot = 3'b011; + 3'd5: anot = 3'b010; + 3'd6: anot = 3'b001; // Same so folds with 7 + endcase + end + + always @ (posedge clk) begin + if (!_ranit) begin + _ranit <= 1; + rnd <= 1; + $write("[%0t] t_case: Running\n", $time); + // + a = 3'b101; + b = 3'b111; + // verilator lint_off CASEX + casex (a) + default: $stop; + 3'bx1x: $stop; + 3'b100: $stop; + 3'bx01: ; + endcase + casez (a) + default: $stop; + 3'b?1?: $stop; + 3'b100: $stop; + 3'b?01: ; + endcase + casez (a) + default: $stop; + {1'b0, twounkn}: $stop; + {1'b1, twounkn}: ; + endcase + casez (b) + default: $stop; + {1'b0, twounkn}: $stop; + {1'b1, twounkn}: ; +// {1'b0, 2'b??}: $stop; +// {1'b1, 2'b??}: ; + endcase + case(a[0]) + default: ; + endcase + casex(a) + default: ; + 3'b?0?: ; + endcase + // verilator lint_off CASEX + //This is illegal, the default occurs before the statements. + //case(a[0]) + // default: $stop; + // 1'b1: ; + //endcase + // + wide = 32'h12345678; + casez (wide) + default: $stop; + 32'h12345677, + 32'h12345678, + 32'h12345679: ; + endcase + // + ok = 0; + casez ({sigone1,sigone2}) + //2'b10, 2'b01, 2'bXX: ; // verilator bails at this since in 2 state it can be true... + 2'b10, 2'b01: ; + 2'b00: ; + default: ok=1'b1; + endcase + if (ok !== 1'b1) $stop; + // + + if (rnd) begin + // This gets covered + $write(""); + end + if (rnd) begin + // This doesn't + // verilator coverage_block_off + $write(""); + end + // + $write("[%0t] t_case: Passed\n", $time); + passed <= 1'b1; + end + end + + // Check parameters in case statements + parameter ALU_DO_REGISTER = 3'h1; // input selected by reg addr. + parameter DSP_REGISTER_V = 6'h03; + + reg [2:0] alu_ctl_2s; // Delayed version of alu_ctl + reg [5:0] reg_addr_2s; // Delayed version of reg_addr + reg [7:0] ir_slave_2s; // Instruction Register delayed 2 phases + reg [15:10] f_tmp_2s; // Delayed copy of F + reg p00_2s; + + initial begin + alu_ctl_2s = 3'h1; + reg_addr_2s = 6'h3; + ir_slave_2s= 0; + f_tmp_2s= 0; + casex ({alu_ctl_2s,reg_addr_2s, + ir_slave_2s[7],ir_slave_2s[5:4],ir_slave_2s[1:0], + f_tmp_2s[11:10]}) + default: p00_2s = 1'b0; + {ALU_DO_REGISTER,DSP_REGISTER_V,1'bx,2'bx,2'bx,2'bx}: p00_2s = 1'b1; + endcase + if (1'b0) $display ("%x %x %x %x", alu_ctl_2s, ir_slave_2s, f_tmp_2s, p00_2s); //Prevent unused + // + case ({1'b1, 1'b1}) + default: $stop; + {1'b1, p00_2s}: ; + endcase + end + + // Check wide overlapping cases + // surefire lint_off CSEOVR + parameter ANY_STATE = 7'h??; + reg [19:0] foo; + initial begin + foo = {1'b0,1'b0,1'b0,1'b0,1'b0,7'h04,8'b0}; + casez (foo) + default: $stop; + {1'b1,1'b?,1'b?,1'b?,1'b?,ANY_STATE,8'b?}: $stop; + {1'b?,1'b1,1'b?,1'b?,1'b?,7'h00,8'b?}: $stop; + {1'b?,1'b?,1'b1,1'b?,1'b?,7'h00,8'b?}: $stop; + {1'b?,1'b?,1'b?,1'b1,1'b?,7'h00,8'b?}: $stop; + {1'b?,1'b?,1'b?,1'b?,1'b?,7'h04,8'b?}: ; + {1'b?,1'b?,1'b?,1'b?,1'b?,7'h06,8'hdf}: $stop; + {1'b?,1'b?,1'b?,1'b?,1'b?,7'h06,8'h00}: $stop; + endcase + end + initial begin + foo = 20'b1010; + casex (foo[3:0]) + default: $stop; + 4'b0xxx, + 4'b100x, + 4'b11xx: $stop; + 4'b1010: ; + endcase + end + initial begin + foo = 20'b1010; + ok = 1'b0; + // Test of RANGE(CONCAT reductions... + casex ({foo[3:2],foo[1:0],foo[3]}) + 5'bxx10x: begin ok=1'b0; foo=20'd1; ok=1'b1; end // Check multiple expressions + 5'bxx00x: $stop; + 5'bxx01x: $stop; + 5'bxx11x: $stop; + endcase + if (!ok) $stop; + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_v/t_chg.v b/test_v/t_chg.v new file mode 100644 index 000000000..8708260c5 --- /dev/null +++ b/test_v/t_chg.v @@ -0,0 +1,79 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t_chg (/*AUTOARG*/ + // Outputs + passed, + // Inputs + clk, fastclk + ); + + input clk; + input fastclk; // surefire lint_off_line UDDIXN + output passed; reg passed; initial passed = 0; + + integer _mode; initial _mode=0; + + reg [31:0] ord1; initial ord1 = 32'h1111; + wire [31:0] ord2; + reg [31:0] ord3; + wire [31:0] ord4; + wire [31:0] ord5; + wire [31:0] ord6; + wire [31:0] ord7; + + // verilator lint_off UNOPT + t_chg_a a ( + .a(ord1), .a_p1(ord2), + .b(ord4), .b_p1(ord5), + .c(ord3), .c_p1(ord4), + .d(ord6), .d_p1(ord7) + ); + + // surefire lint_off ASWEMB + assign ord6 = ord5 + 1; + // verilator lint_on UNOPT + + always @ (/*AS*/ord2) ord3 = ord2 + 1; + + always @ (fastclk) begin // surefire lint_off_line ALWLTR ALWMTR + if (_mode==1) begin + //$write("[%0t] t_chg: %d: Values: %x %x %x %x %x %x %x\n",$time,fastclk,ord1,ord2,ord3,ord4,ord5,ord6,ord7); + //if (ord2 == 2 && ord7 != 7) $stop; + end + end + + always @ (posedge clk) begin + if (_mode==0) begin + $write("[%0t] t_chg: Running\n", $time); + _mode<=1; + ord1 <= 1; + end + else if (_mode==1) begin + _mode<=2; + if (ord7 !== 7) $stop; + $write("[%0t] t_chg: Passed\n", $time); + passed <= 1'b1; + end + end + +endmodule + +module t_chg_a (/*AUTOARG*/ + // Outputs + a_p1, b_p1, c_p1, d_p1, + // Inputs + a, b, c, d + ); + input [31:0] a; output [31:0] a_p1; wire [31:0] a_p1 = a + 1; + input [31:0] b; output [31:0] b_p1; wire [31:0] b_p1 = b + 1; + input [31:0] c; output [31:0] c_p1; wire [31:0] c_p1 = c + 1; + input [31:0] d; output [31:0] d_p1; wire [31:0] d_p1 = d + 1; +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_v/t_clk.v b/test_v/t_clk.v new file mode 100644 index 000000000..e29623cb2 --- /dev/null +++ b/test_v/t_clk.v @@ -0,0 +1,119 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t_clk (/*AUTOARG*/ + // Outputs + passed, + // Inputs + fastclk, clk, reset_l + ); + + input fastclk; + input clk; + input reset_l; + output passed; reg passed; initial passed = 0; + // surefire lint_off STMINI + // surefire lint_off CWECSB + // surefire lint_off NBAJAM + reg _ranit; initial _ranit=0; + // surefire lint_off UDDSMX + reg [7:0] clk_clocks; initial clk_clocks = 0; // surefire lint_off_line WRTWRT + wire [7:0] clk_clocks_d1r; + wire [7:0] clk_clocks_d1sr; + wire [7:0] clk_clocks_cp2_d1r; + wire [7:0] clk_clocks_cp2_d1sr; + // verilator lint_off MULTIDRIVEN + reg [7:0] int_clocks; initial int_clocks = 0; + // verilator lint_on MULTIDRIVEN + reg [7:0] int_clocks_copy; + + // verilator lint_off GENCLK + reg internal_clk; initial internal_clk = 0; + reg reset_int_; + // verilator lint_on GENCLK + + always @ (posedge clk) begin + //$write("CLK1 %x\n", reset_l); + if (!reset_l) begin + clk_clocks <= 0; + int_clocks <= 0; + internal_clk <= 1'b1; + reset_int_ <= 0; + end + else begin + internal_clk <= ~internal_clk; + if (!_ranit) begin + _ranit <= 1; + $write("[%0t] t_clk: Running\n",$time); + reset_int_ <= 1; + end + end + end + + reg [7:0] sig_rst; + always @ (posedge clk or negedge reset_l) begin + //$write("CLK2 %x sr=%x\n", reset_l, sig_rst); + if (!reset_l) begin + sig_rst <= 0; + end + else begin + sig_rst <= sig_rst + 1; // surefire lint_off_line ASWIBB + end + end + + always @ (posedge clk) begin + //$write("CLK3 %x cc=%x sr=%x\n", reset_l, clk_clocks, sig_rst); + if (!reset_l) begin + clk_clocks <= 0; + end + else begin + clk_clocks <= clk_clocks + 8'd1; + if (clk_clocks == 4) begin + if (sig_rst !== 4) $stop; + if (clk_clocks_d1r !== 3) $stop; + if (int_clocks !== 2) $stop; + if (int_clocks_copy !== 2) $stop; + if (clk_clocks_d1r !== clk_clocks_cp2_d1r) $stop; + if (clk_clocks_d1sr !== clk_clocks_cp2_d1sr) $stop; + passed <= 1'b1; + $write("[%0t] t_clk: Passed\n",$time); + end + end + end + + reg [7:0] resetted; + always @ (posedge clk or negedge reset_int_) begin + //$write("CLK4 %x\n", reset_l); + if (!reset_int_) begin + resetted <= 0; + end + else begin + resetted <= resetted + 8'd1; + end + end + + always @ (int_clocks) begin + int_clocks_copy = int_clocks; + end + + always @ (negedge internal_clk) begin + int_clocks <= int_clocks + 8'd1; + end + + t_clk_flop flopa (.clk(clk), .clk2(fastclk), .a(clk_clocks), + .q(clk_clocks_d1r), .q2(clk_clocks_d1sr)); + t_clk_flop flopb (.clk(clk), .clk2(fastclk), .a(clk_clocks), + .q(clk_clocks_cp2_d1r), .q2(clk_clocks_cp2_d1sr)); + t_clk_two two (/*AUTOINST*/ + // Inputs + .fastclk (fastclk), + .reset_l (reset_l)); + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_v/t_clk_flop.v b/test_v/t_clk_flop.v new file mode 100644 index 000000000..e5a19ff4c --- /dev/null +++ b/test_v/t_clk_flop.v @@ -0,0 +1,29 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +`include "verilated.v" + +module t_clk_flop (/*AUTOARG*/ + // Outputs + q, q2, + // Inputs + clk, clk2, a + ); + parameter WIDTH=8; + input clk; + input clk2; + input [(WIDTH-1):0] a; + output [(WIDTH-1):0] q; + output [(WIDTH-1):0] q2; + reg [(WIDTH-1):0] q; + reg [(WIDTH-1):0] q2; + always @ (posedge clk) q<=a; + always @ (posedge clk2) q2<=a; +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_v/t_clk_two.v b/test_v/t_clk_two.v new file mode 100644 index 000000000..a01f2d172 --- /dev/null +++ b/test_v/t_clk_two.v @@ -0,0 +1,33 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +`include "verilated.v" + +module t_clk_two (/*AUTOARG*/ + // Inputs + fastclk, reset_l + ); + input fastclk; + input reset_l; + // verilator lint_off GENCLK + reg clk2; + // verilator lint_on GENCLK + reg [31:0] count; + + wire reset_h = ~reset_l; + always @ (posedge fastclk) begin + if (reset_h) clk2 <= 0; + else clk2 <= ~clk2; + end + always @ (posedge clk2) begin + if (reset_h) count <= 0; + else count <= count + 1; + end +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_v/t_equal.v b/test_v/t_equal.v new file mode 100644 index 000000000..48282ac52 --- /dev/null +++ b/test_v/t_equal.v @@ -0,0 +1,73 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t_equal(/*AUTOARG*/ + // Outputs + passed, + // Inputs + clk + ); + + input clk; + output passed; reg passed; initial passed = 0; + + integer _mode; + + reg _guard1; + reg [127:0] r_wide0; + reg _guard2; + wire [63:0] r_wide1; + reg _guard3; + reg _guard4; + reg _guard5; + reg _guard6; + + assign r_wide1 = r_wide0[127:64]; + + // surefire lint_off STMINI + initial _mode = 0; + + always @ (posedge clk) begin + if (_mode==0) begin + $write("[%0t] t_equal: Running\n", $time); + _guard1 <= 0; + _guard2 <= 0; + _guard3 <= 0; + _guard4 <= 0; + _guard5 <= 0; + _guard6 <= 0; + + _mode<=1; + r_wide0 <= {32'h aa111111,32'hbb222222,32'hcc333333,32'hdd444444}; + end + else if (_mode==1) begin + _mode<=2; + // + if (5'd10 != 5'b1010) $stop; + if (5'd10 != 5'd10) $stop; + if (5'd10 != 5'ha) $stop; + if (5'd10 != 5'o12) $stop; + if (5'd10 != 5'B 1010) $stop; + if (5'd10 != 5'D10) $stop; + if (5'd10 != 5'H a) $stop; + if (5'd10 != 5 'O 12) $stop; + // + if (r_wide0 !== {32'haa111111,32'hbb222222,32'hcc333333,32'hdd444444}) $stop; + if (r_wide1 !== {32'haa111111,32'hbb222222}) $stop; + if (|{_guard1,_guard2,_guard3,_guard4,_guard5,_guard6}) begin + $write("Guard error %x %x %x %x %x\n",_guard1,_guard2,_guard3,_guard4,_guard5); + $stop; + end + $write("[%0t] t_equal: Passed\n", $time); + passed <= 1'b1; + end + end + +endmodule + +// Local Variables: +// compile-command: "./vlint __FILE__" +// End: diff --git a/test_v/t_extend.v b/test_v/t_extend.v new file mode 100644 index 000000000..5e2c8c1a1 --- /dev/null +++ b/test_v/t_extend.v @@ -0,0 +1,83 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t_extend (/*AUTOARG*/ + // Outputs + passed, + // Inputs + clk + ); + + /*verilator public_module*/ + + input clk; + output passed; reg passed; initial passed = 0; + // No verilator_public needed, because it's outside the "" in the $c statement + reg [7:0] cyc; initial cyc=0; + reg c_worked; + reg [8:0] c_wider; + + wire one = 1'b1; + + always @ (posedge clk) begin + cyc <= cyc+8'd1; + + // coverage testing + if (one) begin end + if (!one) begin end + if (cyc[0]) begin end if (!cyc[0]) begin end // multiple on a line + + if (cyc == 8'd1) begin + c_worked <= 0; + end + if (cyc == 8'd2) begin +`ifdef verilator + $c("cout<<\"Calling $c, calling $c...\"<> i); // surefire lint_off_line LATASS + +endmodule diff --git a/test_v/t_initial.v b/test_v/t_initial.v new file mode 100644 index 000000000..3d170889e --- /dev/null +++ b/test_v/t_initial.v @@ -0,0 +1,54 @@ +// $Id:$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +module t_initial(/*AUTOARG*/ + // Outputs + passed, + // Inputs + clk + ); + input clk; + output passed; reg passed; initial passed = 0; + reg _ranit; + + `include "t_initial_inc.v" + + // surefire lint_off STMINI + initial assign user_loaded_value = 1; + + initial _ranit = 0; + + always @ (posedge clk) begin + if (!_ranit) begin + _ranit <= 1; + $write("[%0t] t_initial: Running\n",$time); + + // Test $time + // surefire lint_off CWECBB + if ($time<20) $write("time<20\n"); + // surefire lint_on CWECBB + + // Test $write + $write ("[%0t] %m: User loaded ", $time); + $display ("%b", user_loaded_value); + if (user_loaded_value!=1) $stop; + + // Test $c +`ifdef verilator + $c ("cout<<\"Hi From C++\"< vcs_passed.log + +###################################################################### + +nonc: + @echo "No NC-Verilog simulator installed." + @echo "Not running NC-Verilog regression test." + +nc: nc_passed.log + +nc_passed.log: $(V_FILES) ../test_v/input.vc + ncverilog +define+ncverilog=1 +licqueue -f ../test_v/input.vc -q bench.v + -rm -f nc_passed.log + grep -q Finished ncverilog.log && grep Finished ncverilog.log > nc_passed.log + +###################################################################### + +maintainer-copy:: +clean mostlyclean distclean maintainer-clean:: + -rm -rf obj_dir *.log *.dmp *.vpd simv* vcs.key csrc INCA_libs + diff --git a/test_vcs/bench.v b/test_vcs/bench.v new file mode 100644 index 000000000..7ac3bc7f9 --- /dev/null +++ b/test_vcs/bench.v @@ -0,0 +1,82 @@ +// $Id:$ +// DESCRIPTION: Verilator Test: Top level testbench for VCS or other fully Verilog compliant simulators +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2003 by Wilson Snyder. + +`timescale 1 ns / 1ns + +module bench; + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [39:0] out_quad; // From top of top.v + wire [1:0] out_small; // From top of top.v + wire [69:0] out_wide; // From top of top.v + wire passed; // From top of top.v + // End of automatics + + reg clk; + reg fastclk; + reg reset_l; + reg [1:0] in_small; + reg [39:0] in_quad; + reg [69:0] in_wide; + + // Test cases + top top (/*AUTOINST*/ + // Outputs + .passed (passed), + .out_small (out_small[1:0]), + .out_quad (out_quad[39:0]), + .out_wide (out_wide[69:0]), + // Inputs + .clk (clk), + .fastclk (fastclk), + .reset_l (reset_l), + .in_small (in_small[1:0]), + .in_quad (in_quad[39:0]), + .in_wide (in_wide[69:0])); + + //surefire lint_off STMINI + //surefire lint_off STMFVR + //surefire lint_off DLYONE + + integer fh; + + // surefire lint_off CWECBB + initial begin + reset_l = 1'b1; // Want to catch negedge + fastclk = 0; + clk = 0; + forever begin + in_small = 0; + in_wide = 0; + $write("[%0t] %x %x %x %x %x\n", $time, clk, reset_l, passed, out_small, out_wide); + if (($time % 10) == 3) clk = 1'b1; + if (($time % 10) == 8) clk = 1'b0; + if ($time>10) reset_l = 1'b1; + else if ($time > 1) reset_l = 1'b0; + if ($time>60 || passed === 1'b1) begin + if (passed !== 1'b1) begin + $write("A Test failed!!!\n"); + $stop; + end + else begin + $write("*-* All Finished *-*\n"); // Magic if using perl's Log::Detect + fh = $fopen("test_passed.log"); + $fclose(fh); + end + $finish; + end + #1; + fastclk = !fastclk; + end + end + +endmodule + +// Local Variables: +// verilog-library-directories:("." "../test_v") +// compile-command: "vlint --brief -f ../test_v/input.vc bench.v" +// End: diff --git a/test_verilated/.cvsignore b/test_verilated/.cvsignore new file mode 100644 index 000000000..8ae794a39 --- /dev/null +++ b/test_verilated/.cvsignore @@ -0,0 +1,8 @@ +*.old +obj_dir +vgen.v +simv* +*.key +csrc +*.log +INCA_libs diff --git a/test_verilated/Makefile b/test_verilated/Makefile new file mode 100644 index 000000000..932455ba0 --- /dev/null +++ b/test_verilated/Makefile @@ -0,0 +1,117 @@ +# $Id$ */ +#***************************************************************************** +# +# DESCRIPTION: Verilator Example: Makefile for inside source directory +# +# This calls the object directory makefile. That allows the objects to +# be placed in the "current directory" which simplifies the Makefile. +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. +# +#****************************************************************************/ + +# This must point to the root of the VERILATOR kit +VERILATOR_ROOT := $(shell pwd)/.. +export VERILATOR_ROOT +PERL = perl +PWD := $(shell pwd) + +VERILATOR_SW += +ifeq ($(VERILATOR_NO_DEBUG),) + VERILATOR_SW += --debug +endif + +PERL_PACKAGES_OK := $(shell $(PERL) -e 'eval "use Bit::Vector; print 1;";') + +###################################################################### + +default: test + +ifneq ($(PERL_PACKAGES_OK),1) +test:: nopackages +else + ifneq ($(VCS_HOME),) +test:: vcs + else +test:: novcs + endif + + ifneq ($(NC_ROOT),) +test:: nc + else +test:: nonc + endif + +test:: v3 +endif + +vgen.v: ./vgen.pl + $(PERL) vgen.pl $(VGEN_FLAGS) + +# We ulimit cpu time, as some cases make gcc 3.3.4 hang +random: + -rm -rf obj_dir/Vgen* obj_dir/simx + $(PERL) vgen.pl --seed=0 --numops=1000 --depth=4 --raise=4 + VERILATOR_NO_DEBUG=1 CPPFLAGS_ADD=-Wno-error VCS_HOME= NC_ROOT= bash -c "ulimit -t 120; $(MAKE) test" +# $(MAKE) nc + +random_forever: + while ( $(MAKE) random ) ; do \ + echo ; \ + done + +###################################################################### + +nopackages: + @echo "No perl Bit::Vector package installed." + @echo "Not running regression test." + +novcs: + @echo "No VCS simulator installed." + @echo "Not running VCS regression test." + +vcs: vcs_passed.log + +simv: vgen.v sim_main.v + vcs +cli -I +define+vcs+1 +v2k -q vgen.v sim_main.v + +vcs_passed.log : simv + -rm -f vcs_passed.log + ./simv -l sim.log + grep -q Finished sim.log && grep Finished sim.log > vcs_passed.log + +###################################################################### + +nonc: + @echo "No NC-Verilog simulator installed." + @echo "Not running NC-Verilog regression test." + +nc: nc_passed.log + +nc_passed.log: vgen.v sim_main.v + ncverilog +licqueue +define+ncverilog=1 -q vgen.v sim_main.v + -rm -f nc_passed.log + grep -q Finished ncverilog.log && grep Finished ncverilog.log > nc_passed.log + +###################################################################### + +v3: prep compile v3_passed.log + +prep: vgen.v $(VERILATOR_ROOT)/bin/verilator + $(PERL) $(VERILATOR_ROOT)/bin/verilator $(VERILATOR_SW) --cc vgen.v + +compile: + cd obj_dir ; $(MAKE) -j 3 -f ../Makefile_obj + +v3_passed.log v3_run: prep compile + -rm -f v3_passed.log sim.log + obj_dir/simx | tee sim.log + grep -q Finished sim.log && grep Finished sim.log > v3_passed.log + +###################################################################### +maintainer-copy:: +clean mostlyclean distclean maintainer-clean:: + -rm -rf obj_dir *.log *.dmp *.vpd simv* *.key vgen.v csrc INCA_libs + diff --git a/test_verilated/Makefile_obj b/test_verilated/Makefile_obj new file mode 100644 index 000000000..ac645f625 --- /dev/null +++ b/test_verilated/Makefile_obj @@ -0,0 +1,37 @@ +# $Id:$ -*- Makefile -*- +#***************************************************************************** +# +# DESCRIPTION: Verilator Example: Makefile for inside object directory +# +# This executed in the object directory, and called by ../Makefile +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. +# +#***************************************************************************** + +default: simx +include Vvgen.mk + +####################################################################### +# Use sp_log.cpp, so we can get output in sim.log + +CPPFLAGS += -DUTIL_PRINTF=sp_log_printf + +# Needed by tracing routines +CPPFLAGS += -DWAVES=1 +CPPFLAGS += -DVL_DEBUG=1 +CPPFLAGS += $(CPPFLAGS_ADD) + +####################################################################### +# Linking final exe -- presumes have a sim_main.cpp + +SP_SRCS = verilated.o +# If you are using the older version of SystemPerl, you may want instead: +#SP_SRCS = sp_log.o SpTraceVcd.o + +simx: sim_main.o $(SP_SRCS) $(VM_PREFIX)__ALL.a + $(CXX) $(LDFLAGS) -g $^ $(LOADLIBES) $(LDLIBS) -o $@ $(LIBS) 2>&1 | c++filt + +sim_main.o: sim_main.cpp $(VM_PREFIX).h diff --git a/test_verilated/sim_main.cpp b/test_verilated/sim_main.cpp new file mode 100644 index 000000000..e44cf0286 --- /dev/null +++ b/test_verilated/sim_main.cpp @@ -0,0 +1,54 @@ +// $Id$ +// DESCRIPTION: Verilator Test: Top level main for invoking model +// +// 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. + +#include + +#include "Vvgen.h" +#include "Vvgen_vgen.h" // For v + +Vvgen *top; + +unsigned int main_time = 0; + +double sc_time_stamp () { + return main_time; +} + +int main(int argc, char **argv, char **env) { + Verilated::debug(0); // We compiled with it on for testing, turn it back off + + top = new Vvgen; + top->check = 0; + top->clk = 0; + +#define CYCTIME 10 + + // Cycle the interpreter + while (main_time < CYCTIME*top->v->CYCLES) { + top->eval(); + main_time += CYCTIME/2; + top->clk = !top->clk; + top->eval(); + main_time += CYCTIME/2; + top->clk = !top->clk; + } + + // Do a checking run + top->check = 1; + for (int i=0; i<10; i++) { + top->eval(); + main_time += CYCTIME/2; + top->clk = !top->clk; + top->eval(); + main_time += CYCTIME/2; + top->clk = !top->clk; + } + + top->final(); + + exit(0L); +} diff --git a/test_verilated/sim_main.v b/test_verilated/sim_main.v new file mode 100644 index 000000000..abe45cde6 --- /dev/null +++ b/test_verilated/sim_main.v @@ -0,0 +1,42 @@ +// $Id$ +// DESCRIPTION: Verilator Test: Top level main for invoking model +// +// 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. + +module sim_main; + /*verilator public_module*/ + + reg clk; + reg check; + wire done; + + vgen vgen (/*AUTOINST*/ + // Outputs + .done (done), + // Inputs + .clk (clk), + .check (check)); + + integer i; + + initial begin + check = 1'b0; + clk = 1'b0; + for (i=0; i<10*vgen.CYCLES; i=i+1) begin + #5; + clk = ~clk; + #5; + clk = ~clk; + end + check = 1'b1; + for (i=0; i<10; i=i+1) begin + #5; + clk = ~clk; + #5; + clk = ~clk; + end + end + +endmodule diff --git a/test_verilated/vgen.pl b/test_verilated/vgen.pl new file mode 100755 index 000000000..6f6e16e03 --- /dev/null +++ b/test_verilated/vgen.pl @@ -0,0 +1,1070 @@ +#!/usr/bin/perl -w +#$Id$ +###################################################################### +# +# This program is 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. +# +# 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 Bit::Vector; +use strict; +use vars qw ($Debug); + +our @Orig_ARGV = @ARGV; +our $Rerun_Args = $0." ".join(' ',@Orig_ARGV); + +use vars qw (@Blocks + %Vars + %VarsBlock + %Tree + @Commit + $Depth + %IdWidth + %Ops); + +#====================================================================== + +# width=> Number of bits the output size is, 0=you tell me. +# func=> What to put in output file +# signed=> 0=unsigned output, 1=signed output, '%1'=signed if op1 signed +# em=> How to calculate emulated return value +# %w Width of this output op ($treeref->{width}) +# %v Output value ($treeref->{val}) +# %1r First operand ($treeref->{op1}) +# %1v First operand value ($treeref->{op1}{val}) +# %1w First operand width ($treeref->{op1}{width}) + +our $Raise_Weight_Max = 50; +%Ops = +( + 'VCONST'=> {weight=>1&&20, width=>0, sc=>1, terminal=>1, v=>'%v', }, + 'VIDNEW'=> {weight=>1&&10, width=>0, sc=>1, terminal=>0, v=>'%i', }, + 'VIDOLD'=> {weight=>1&&20, width=>0, sc=>1, terminal=>0, v=>'%i', }, + 'VRANGE'=> {weight=>1&&30, width=>0, signed=>0,sc=>0, terminal=>0, v=>'%i[%2:%3]', }, + 'VBITSEL'=> {weight=>1&&10, width=>1, signed=>0,sc=>0, terminal=>0, v=>'%i[%2]', }, + 'VBITSELP'=> {weight=>1&&10, width=>0, signed=>0,sc=>0, terminal=>0, v=>'%i[%2+:%3]', }, + 'VBITSELM'=> {weight=>1&&10, width=>0, signed=>0,sc=>0, terminal=>0, v=>'%i[%2-:%3]', }, + # Unary + 'VEXTEND'=> {weight=>1&&3, width=>-2, signed=>0,sc=>0, terminal=>0, v=>'{%xw\'h0,%1}', }, + 'VLOGNOT'=> {weight=>1&&1, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(! %1)', }, + 'VREDAND'=> {weight=>1&&1, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(& %1)', }, + 'VREDOR'=> {weight=>1&&1, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(| %1)', }, + 'VREDNAND'=> {weight=>1&&1, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(~& %1)', }, + 'VREDNOR'=> {weight=>1&&1, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(~| %1)', }, + 'VREDXNOR'=> {weight=>1&&1, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(^~ %1)', }, + 'VREDXOR'=> {weight=>1&&1, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(^ %1)', }, + 'VNOT'=> {weight=>1&&3, width=>0, sc=>1, terminal=>0, v=>'(~ %1)', }, + 'VUNARYMIN'=> {weight=>1&&2, width=>0, sc=>1, terminal=>0, v=>'(- %1)', }, + 'VCOUNTONES'=> {weight=>0&&2, width=>32, signed=>0, sc=>0, terminal=>0, v=>'\$countones(%1)', }, # No ncv support + 'VONEHOT'=> {weight=>0&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'\$onehot(%1)', }, # No ncv support + 'VONEHOT0'=> {weight=>0&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'\$onehot0(%1)', }, # No ncv support + # Binary + 'VAND'=> {weight=>1&&2, width=>0, sc=>1, terminal=>0, v=>'(%1 & %2)', }, + 'VOR'=> {weight=>1&&2, width=>0, sc=>1, terminal=>0, v=>'(%1 | %2)', }, + 'VNAND'=> {weight=>1&&0, width=>0, sc=>0, terminal=>0, v=>'(%1 ~& %2)', }, #FIX vcs bug! + 'VNOR'=> {weight=>1&&0, width=>0, sc=>0, terminal=>0, v=>'(%1 ~| %2)', }, #FIX vcs bug! + 'VXOR'=> {weight=>1&&2, width=>0, sc=>1, terminal=>0, v=>'(%1 ^ %2)', }, + 'VXNOR'=> {weight=>1&&0, width=>0, sc=>0, terminal=>0, v=>'(%1 ^~ %2)', }, #FIX vcs bug! + 'VEQ'=> {weight=>1&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(%1 == %2)', }, + 'VNEQ'=> {weight=>1&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(%1 != %2)', }, + 'VGT'=> {weight=>1&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(%1 > %2)', }, + 'VGTE'=> {weight=>1&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(%1 >= %2)', }, + 'VLT'=> {weight=>1&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(%1 < %2)', }, + 'VLTE'=> {weight=>1&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(%1 <= %2)', }, + 'VEQCASE'=> {weight=>1&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(%1 === %2)', }, # FIX just a = for now + 'VNEQCASE'=> {weight=>1&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(%1 !== %2)', }, # FIX just a != for now + 'VLOGOR'=> {weight=>1&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(%1 || %2)', }, + 'VLOGAND'=> {weight=>1&&2, width=>1, signed=>0, sc=>0, terminal=>0, v=>'(%1 && %2)', }, + 'VADD'=> {weight=>1&&10, width=>0, sc=>1, terminal=>0, v=>'(%1 + %2)', }, + 'VSUB'=> {weight=>1&&10, width=>0, sc=>1, terminal=>0, v=>'(%1 - %2)', }, + 'VMUL'=> {weight=>1&&15,width=>0, sc=>1, terminal=>0, v=>'(%1 * %2)', }, # High % as rarely applyable + #'VDIV'=> {weight=>2&&0, width=>-32, sc=>1, terminal=>0, v=>'(%1 / %2)', }, # FIX + #'VMODDIV'=> {weight=>2&&0, width=>-32, sc=>1, terminal=>0, v=>'(%1 %% %2)', }, # FIX + #'VPOW'=> {weight=>2&&0,width=>-64, sc=>0, terminal=>0, v=>'(%1 ** %2)', }, + 'VSHIFTL'=> {weight=>1&&8, width=>0, signed=>0, sc=>0, terminal=>0, v=>'(%1 << %2)', }, + 'VSHIFTLS'=> {weight=>1&&8, width=>0, signed=>1, sc=>0, terminal=>0, v=>'(%1 <<< %2)', }, + 'VSHIFTR'=> {weight=>1&&8, width=>0, signed=>0, sc=>0, terminal=>0, v=>'(%1 >> %2)', }, + 'VSHIFTRS'=> {weight=>1&&15,width=>0, signed=>1, sc=>0, terminal=>0, v=>'(%1 >>> %2)', }, # ShiftR seems to sign extend differently for <=32 and >32 bits + 'VCONCAT'=> {weight=>1&&4, width=>-2,signed=>0, sc=>0, terminal=>0, v=>'{%1,%2}', }, + 'VREPLIC'=> {weight=>1&&2, width=>0, signed=>0, sc=>0, terminal=>0, v=>'{%1{%2}}', }, + 'VREPLIC1W'=> {weight=>1&&2, width=>0, signed=>0, sc=>0, terminal=>0, v=>'{%1{%2}}', }, + 'VSIGNED'=> {weight=>1&&2, width=>0, signed=>1, sc=>0, terminal=>0, v=>'\$signed(%1)', }, + 'VUNSIGNED'=> {weight=>1&&2, width=>0, signed=>0, sc=>0, terminal=>0, v=>'\$unsigned(%1)', }, + # Triops + 'VCOND'=> {weight=>1&&4, width=>0, sc=>0, terminal=>0, v=>'(%1 ? %2 : %3)', }, + # Control flow + #VIF + #VFOR + #VCASE + #VCASEX + #VCASEZ + ); + +my %ops2 = +( + 'VCONST'=> {pl=>'', rnd=>'rnd_const(%tr);'}, + 'VIDNEW'=> {pl=>'%tv=$Vars{%i}{val};', + rnd=>'%i=next_id(%tw); $Vars{%i}=gen_leaf(width=>%tw,trunc=>1,signed=>%tg); id_commit(%tr,"%i");1;',}, + 'VIDOLD'=> {pl=>'%tv=$Vars{%i}{val};', rnd=>'%i=old_id(%tr);', ok_id_width=>1,}, + 'VRANGE'=> {pl=>'VRANGE(%tr,$Vars{%i}{val},%2v,%3v);', rnd=>'%i=next_id(%tw); my $lsb=rnd(128-%tw); my $msb=$lsb+%tw-1; %2r=val_leaf($msb); %3r=val_leaf($lsb); $Vars{%i}=gen_leaf(width=>($msb+1));'}, + 'VBITSEL'=> {pl=>'VRANGE(%tr,$Vars{%i}{val},%2v,%2v);', rnd=>'%i=next_id(%tw); my $wid=min(128,rnd_width()|3); %2r=gen_leaf(width=>(log2($wid)-1),signed=>0); $Vars{%i}=gen_leaf(width=>$wid);'}, + 'VBITSELP'=> {pl=>'VBITSELP(%tr,$Vars{%i}{val},%2v,%3v);', rnd=>'%i=next_id(%tw); my $wid=min(128,(%tw+rnd_width()|3)); %3r=val_leaf(%tw); my $maxval = $wid-%tw; %2r=(($maxval<4)?val_leaf($maxval):gen_leaf(width=>(log2($maxval)-1),signed=>0)); $Vars{%i}=gen_leaf(width=>$wid);'}, + 'VBITSELM'=> {pl=>'VBITSELM(%tr,$Vars{%i}{val},%2v,%3v);', rnd=>'%i=next_id(%tw); my $wid=min(128,(%tw+rnd_width()|3)); %3r=val_leaf(%tw); my $maxval = $wid-1; my $minval=%tw-1; %2r=val_leaf(rnd($maxval-$minval)+$minval); $Vars{%i}=gen_leaf(width=>$wid);'}, # No easy way to make expr with specified minimum + # Unary + 'VEXTEND'=> {pl=>'VRESIZE (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>rnd_width(%tw-1));'}, + 'VLOGNOT'=> {pl=>'VLOGNOT (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, + 'VREDAND'=> {pl=>'VREDAND (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, + 'VREDOR'=> {pl=>'VREDOR (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, + 'VREDNAND'=> {pl=>'VREDNAND (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, + 'VREDNOR'=> {pl=>'VREDNOR (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, + 'VREDXOR'=> {pl=>'VREDXOR (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, + 'VREDXNOR'=> {pl=>'VREDXNOR (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, + 'VNOT'=> {pl=>'VNOT (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg);'}, + 'VUNARYMIN'=> {pl=>'VUNARYMIN(%tr,%1v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg);'}, + 'VCOUNTONES'=> {pl=>'VCOUNTONES(%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, + 'VONEHOT'=> {pl=>'VONEHOT (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, + 'VONEHOT0'=> {pl=>'VONEHOT0 (%tr,%1v);', rnd=>'%1r=gen_leaf(width=>0);'}, + # Binary + 'VAND'=> {pl=>'VAND (%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>%tw,signed=>%tg);'}, + 'VOR'=> {pl=>'VOR (%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>%tw,signed=>%tg);'}, + 'VNAND'=> {pl=>'VNAND (%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>%tw,signed=>%tg);'}, + 'VNOR'=> {pl=>'VNOR (%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>%tw,signed=>%tg);'}, + 'VXOR'=> {pl=>'VXOR (%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>%tw,signed=>%tg);'}, + 'VXNOR'=> {pl=>'VXNOR (%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>%tw,signed=>%tg);'}, + 'VEQ'=> {pl=>'VEQ (%tr,%1r,%2r);', rnd=>'%1r=gen_leaf(width=>0); %2r=gen_leaf(width=>%1w);'}, + 'VNEQ'=> {pl=>'VNE (%tr,%1r,%2r);', rnd=>'%1r=gen_leaf(width=>0); %2r=gen_leaf(width=>%1w);'}, + 'VGT'=> {pl=>'VGT (%tr,%1r,%2r);', rnd=>'%1r=gen_leaf(width=>0); %2r=gen_leaf(width=>%1w);'}, + 'VGTE'=> {pl=>'VGE (%tr,%1r,%2r);', rnd=>'%1r=gen_leaf(width=>0); %2r=gen_leaf(width=>%1w);'}, + 'VLT'=> {pl=>'VLT (%tr,%1r,%2r);', rnd=>'%1r=gen_leaf(width=>0); %2r=gen_leaf(width=>%1w);'}, + 'VLTE'=> {pl=>'VLE (%tr,%1r,%2r);', rnd=>'%1r=gen_leaf(width=>0); %2r=gen_leaf(width=>%1w);'}, + 'VEQCASE'=> {pl=>'VEQ (%tr,%1r,%2r);', rnd=>'%1r=gen_leaf(width=>0); %2r=gen_leaf(width=>%1w);'}, + 'VNEQCASE'=> {pl=>'VNE (%tr,%1r,%2r);', rnd=>'%1r=gen_leaf(width=>0); %2r=gen_leaf(width=>%1w);'}, + 'VLOGOR'=> {pl=>'VLOGOR (%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>0); %2r=gen_leaf(width=>0);'}, + 'VLOGAND'=> {pl=>'VLOGAND(%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>0); %2r=gen_leaf(width=>0);'}, + 'VADD'=> {pl=>'VADD (%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>%tw,signed=>%tg);', trunc=>1,}, + 'VSUB'=> {pl=>'VSUB (%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>%tw,signed=>%tg);', trunc=>1,}, + 'VMUL'=> {pl=>'VMUL (%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>%tw,signed=>%tg);', trunc=>1,}, # Multiply generates larger width, so need truncate for safety + #'VDIV'=> {pl=>'VDIV (%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>%tw,signed=>%tg);'}, + #'VMODDIV'=> {pl=>'VMODDIV(%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>%tw,signed=>%tg);'}, + #'VPOW'=> {pl=>'VPOW (%tr,%1r,%2r);', rnd=>'%1r=gen_leaf(width=>min(%tw,6),signed=>%tg); %2r=gen_leaf(width=>min(%tw,8),signed=>%tg);', trunc=>1,}, # Generates larger width, so need truncate for safety + 'VSHIFTL'=> {pl=>'VSHIFTL(%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>log2(%tw)+1,signed=>%tg);'}, + 'VSHIFTLS'=> {pl=>'VSHIFTL(%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>log2(%tw)+1,signed=>%tg);'}, + 'VSHIFTR'=> {pl=>'VSHIFTR(%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>log2(%tw)+1,signed=>%tg);'}, + 'VSHIFTRS'=> {pl=>'VSHIFTRS(%tr,%1v,%2v);', rnd=>'%1r=gen_leaf(width=>%tw,signed=>%tg); %2r=gen_leaf(width=>log2(%tw)+1,signed=>%tg);'}, + 'VCONCAT'=> {pl=>'VCONCAT(%tr,%1v,%2v);', rnd=>'my $d=(rnd(%tw-2)+1); %1r=gen_leaf(width=>$d); %2r=gen_leaf(width=>(%tw-$d));'}, + 'VREPLIC'=> {pl=>'VREPLIC(%tr,%1v,%2v);', rnd=>'my $d=rnd_rep_width(%tw); %1r=val_leaf($d); %2r=gen_leaf(width=>(%tw/$d));'}, + 'VREPLIC1W'=> {pl=>'VREPLIC(%tr,%1v,%2v);', rnd=>'%1r=val_leaf(%tw); %2r=gen_leaf(width=>1);'}, + 'VSIGNED'=> {pl=>'VCLONE (%tr,%1v,0);', rnd=>'%1r=gen_leaf(width=>%tw);'}, + 'VUNSIGNED'=> {pl=>'VCLONE (%tr,%1v,0);', rnd=>'%1r=gen_leaf(width=>%tw);'}, + # Triops + 'VCOND'=> {pl=>'VCOND(%tr,%1v,%2v,%3v);', rnd=>'%1r=gen_leaf(width=>1); %2r=gen_leaf(width=>%tw,signed=>%tg); %3r=gen_leaf(width=>%tw,signed=>%tg);'}, + ); + +foreach my $op (keys %ops2) { + while ((my $key,my $val) = each %{$ops2{$op}}) { + $Ops{$op}{$key} = $val; + } +} + +#====================================================================== +# main + +#Bit::Vector->Configuration("ops=arithmetic"); + + +my $opt_seed=5; +our $Opt_NumOps = 300; +our $Opt_Depth = 4; +our $Opt_Signed = 1; +our $Opt_Raise; +our $Opt_Sc; +our $Opt_BlockStmts = 2; +our $Signed_Pct = 60; +$Debug = 0; +if (! GetOptions ( + "help" => \&usage, + "debug" => \&debug, + "depth=i" => \$Opt_Depth, + "blockstmts=i"=> \$Opt_BlockStmts, + "numops=i" => \$Opt_NumOps, + "raise=i" => \$Opt_Raise, + "seed=i" => \$opt_seed, + "signed!" => \$Opt_Signed, + "sc!" => \$Opt_Sc, + "<>" => \¶meter, + )) { + usage(); +} + +if ($opt_seed==0) { + srand(); + $opt_seed = rnd(1<<20)+1; + $Rerun_Args =~ s/-seed[= ]+0/-seed=$opt_seed/; + print " $Rerun_Args\n"; +} +srand($opt_seed); +init(); +gentest(); +write_output_sc("vgen.cpp") if $Opt_Sc; +write_output_v("vgen.v") if !$Opt_Sc; + +#---------------------------------------------------------------------- + +sub usage { + print '$Id$ ', "\n"; + pod2usage(-verbose=>2, -exitval => 2); + exit (1); +} + +sub debug { + $Debug = 1; +} + +sub parameter { + my $param = shift; + die "%Error: Unknown parameter: $param\n"; +} + +####################################################################### +####################################################################### +####################################################################### +####################################################################### +# Global Functions + +sub init { + for my $op (keys %Ops) { + my $opref = $Ops{$op}; + $opref->{name} = $op; + gen_v($opref); + gen_pl($opref); + gen_rnd($opref); + $opref->{weight} = 0 if $Opt_Sc && !$opref->{sc}; + $opref->{weight} = 2 if $Opt_Sc && $op eq 'VCONST'; + } + raise(); +} + +sub raise { + for (my $i=0; $i<($Opt_Raise||0); $i++) { + my @ops = (values %Ops); + while (1) { + my $rndop = $ops[rnd($#ops + 1)]; + next if !$rndop->{weight}; # Don't turn on disabled ops + $rndop->{weight} += rnd($Raise_Weight_Max); + printf "\tWeight %-15s +%d\n",$rndop->{name},$rndop->{weight}; + last; + } + } +} + +sub gentest { + for (my $opn=0; $opn<$Opt_NumOps/$Opt_BlockStmts; $opn++) { + do_a_test(); + } +} + +####################################################################### +# Randomization + +sub _rnd_op_ok { + my $opref = shift; + my $paramref = shift; + return (($opref->{width} == 0 + || $opref->{width} == $paramref->{width} + || ($opref->{width}==-32 && $paramref->{width}<=32) # -32... must be <32 bits + || ($opref->{width}==-64 && $paramref->{width}<=64) # -64... must be <64 bits + || ($opref->{width}==-2 && $paramref->{width}>=2) # -2... must be >2 bits + ) + && (!$opref->{ok_id_width} || $IdWidth{$paramref->{width}}{$paramref->{signed}||0}) + && (!defined $opref->{signed} || ($opref->{signed} == ($paramref->{signed}||0))) + && (!$opref->{trunc} || $paramref->{trunc}) + && (!$opref->{opt_signed} || $Opt_Signed) + && (($Depth < $Opt_Depth && !$paramref->{need_terminal}) + || $opref->{terminal})); +} + +sub rnd_op { + my $paramref = shift; + + my $totweight = 0; + foreach my $opref (values %Ops) { + if (_rnd_op_ok($opref,$paramref)) { + $totweight += $opref->{weight}; + } + } + my $chooseweight = rnd($totweight); + $totweight = 0; + foreach my $opref (values %Ops) { + if (_rnd_op_ok($opref,$paramref)) { + $totweight += $opref->{weight}; + if ($chooseweight < $totweight) { + return $opref; + } + } + } + die "%Error: No instructions match,"; +} + +sub rnd_width { + my $max = shift; + my $v = rnd(100); + my $n = (0 + || (($v<20) && 1) + || (($v<25) && 2) + || (($v<30) && 31) + || (($v<35) && 32) + || (($v<40) && 63) + || (($v<45) && 64) + || (($v<50) && 95) + || (($v<55) && 96) + || (rnd(128)+1)); + if ($Opt_Sc) { + $n = (0 + #|| (($v<50) && 32) + || (32)); + #(!$max) or die "%Error: --sc max must 32/64/96,"; + } + if ($max && $n>=$max) { $n = rnd($max-1)+1; } + return $n; +} + +sub rnd_rep_width { + my $out = shift; + return 1 if $out==1; + # We'd like to pick any divisor that works. + my @factors; + for (my $div=1; $div<$out; $div++) { + if (int($out/$div)==($out/$div)) { + push @factors, $div; + } + } + my $fac = $factors[rnd($#factors+1)]; + #print "RND REP $out -> $fac (@factors)\n" if $Debug; + return $fac; +} + +sub rnd_const { + my $treeref = shift; + my $width = $treeref->{width} or die; + my $v = rnd(100); + + my $val = Bit::Vector->new($width); + if ($v<25) { # zero + } elsif ($v<50) { # ones + $val->Word_Store(0,~0); + $val->Word_Store(1,~0) if $width>32; + $val->Word_Store(2,~0) if $width>64; + $val->Word_Store(3,~0) if $width>96; + } elsif ($v<60) { # one + $val->Word_Store(0,1); + } else { #random + $val->Word_Store(0,rnd_int()); + $val->Word_Store(1,rnd_int()) if $width>32; + $val->Word_Store(2,rnd_int()) if $width>64; + $val->Word_Store(3,rnd_int()) if $width>96; + } + $treeref->{val} = $val; +} + +sub rnd_int { + my $v = rnd(100); + return 0 if ($v<25); + return ~0 if ($v<50); + return 1 if ($v<60); + return rnd32(); +} + +sub rnd { + return (int(rand($_[0]))) if ($_[0] < (1<<15)); + return (rnd32() % $_[0]); +} +sub rnd32 { + my $vp = int(rand(1<<16)); + $vp ^= (int(rand(1<<8)))<<16;# Single 1<<16 doesn't work + $vp ^= (int(rand(1<<8)))<<24; + return ($vp); +} + +####################################################################### + +our $Next_Id = 0; +sub next_id { + # Note width hasn't been determined yet + $Next_Id++; + my $id = sprintf("W%04d",$Next_Id); + return $id; +} +sub id_commit { + my $treeref = shift; + my $width = $treeref->{width}; + my $signed = $treeref->{signed}; + my $id = shift; + push @Commit, sub { + $IdWidth{$width}{$signed} = [] if !$IdWidth{$width}{$signed}; + push @{$IdWidth{$width}{$signed}}, $id; + $VarsBlock{$id}{set} = 1; + 1; + }; +} + +sub old_id { + my $treeref = shift; + my $width = $treeref->{width}; + my $signed = $treeref->{signed}; + + my $n = $#{$IdWidth{$width}{$signed}} + 1; + my $idn = rnd($n); + my $id = $IdWidth{$width}{$signed}[$idn]; + $VarsBlock{$id}{used} = 1; + return $id; +} + +sub write_output_v { + my $filename = shift; + + my $fh = IO::File->new($filename, "w") or die("%Error: $! $filename,\n"); + print $fh "// Created by: $Rerun_Args\n"; + + print $fh "module vgen (clk, check, done);\n"; + print $fh " input clk;\n"; + print $fh " input check;\n"; + print $fh " output done;\n"; + print $fh ' initial $write("\n*** Vgen.v starting, seed = ',$opt_seed,'\n");',"\n"; + + print $fh " // verilator lint_off UNSIGNED\n"; + print $fh " // verilator lint_off CMPCONST\n"; + print $fh " // verilator lint_off WIDTH\n"; + print $fh "\n"; + + my $cycles = 2; + + foreach my $var (sort (keys %Vars)) { + print $fh "",decl_text ($var),"\n"; + } + + foreach my $block (@Blocks) { + print $fh "\t//".('='x60)."\n"; + my $style = rnd(100); + if ($style < 15) { + # This allows statements to get split up, and constants to propagate + print $fh " always @(", join(" or ", ('check', @{$block->{inputs}})); + print $fh ") begin : $block->{name}\n"; + print $fh @{$block->{preass}}; + print $fh " end\n"; + print $fh " always @(posedge clk) begin : $block->{name}Check\n"; + print $fh @{$block->{body}}; + print $fh " end\n"; + } + elsif ($style < 40) { + print $fh " always @(", join(" or ", ('check', @{$block->{inputs}})); + print $fh ") begin : $block->{name}\n"; + print $fh @{$block->{preass}}; + print $fh @{$block->{body}}; + print $fh " end\n"; + } + else { + foreach my $stmt (@{$block->{preass}}) { + $cycles++; + print $fh " always @(posedge clk) begin\n"; + $stmt =~ s/ = / <= /mg; + print $fh $stmt; + print $fh " end\n"; + } + print $fh " always @(posedge clk) begin\n"; + print $fh @{$block->{body}}; + print $fh " end\n"; + } + } + + print $fh "\n"; + print $fh " reg done; initial done=1'b0;\n"; + print $fh " reg ddone; initial ddone=1'b0;\n"; + print $fh " always @(posedge clk) begin\n"; + print $fh " if (check) begin\n"; + print $fh " done <= 1'b1;\n"; + print $fh " end\n"; + print $fh " if (done && !ddone) begin\n"; + print $fh " ddone <= 1'b1;\n"; + print $fh ' $write("*-* All Finished *-*\n");',"\n"; + print $fh " end\n"; + print $fh " end\n"; + + print $fh "\n"; + print $fh " parameter [31:0] CYCLES /*verilator public*/ = $cycles;\n"; + print $fh "endmodule\n"; + + $fh->close(); +} + +sub write_output_sc { + my $filename = shift; + + my $fh = IO::File->new($filename, "w") or die("%Error: $! $filename,\n"); + + print $fh "// -*- SystemC -*-\n"; + print $fh "// Created by: $Rerun_Args\n"; + + # Classes + foreach my $block (@Blocks) { + print $fh "class $block->{name};\n"; + } + print $fh "\n"; + + # Headers + print $fh "//".('='x60)."\n"; + print $fh "SC_MODULE(Vgen) {\n"; + print $fh "public:\n"; + print $fh " sc_in_clk clk;\n"; + print $fh " sc_in check;\n"; + print $fh " static const int CYCLES /*verilator public*/ = ",$#Blocks+3,";\n"; + print $fh "\n"; + foreach my $var (sort (keys %Vars)) { + print $fh "",decl_text ($var,"sc_signal"),"\n"; + } + print $fh "\n"; + foreach my $block (@Blocks) { + print $fh " $block->{name}* ".lc($block->{name}).";\n"; + } + print $fh " SC_CTOR(Vgen);\n"; + print $fh "};\n\n"; + + # Sub Interface + print $fh "//".('='x60)."\n"; + foreach my $block (@Blocks) { + print $fh "SC_MODULE($block->{name}) {\n"; + print $fh "public:\n"; + print $fh " sc_in_clk clk;\n"; + print $fh " sc_in check;\n"; + foreach my $var (@{$block->{inputs}}) { + print $fh "",decl_text ($var,"sc_in"),"\n"; + } + foreach my $var (@{$block->{outputs}}) { + print $fh "",decl_text ($var,"sc_out"),"\n"; + } + print $fh " SC_CTOR($block->{name});\n"; + print $fh " void clkPosedge();\n"; + print $fh "};\n\n"; + } + + # Implementation + print $fh "//".('='x60)."\n"; + print $fh "SP_CTOR_IMP(Vgen) : clk(\"clk\"), check(\"check\") {\n"; + foreach my $block (@Blocks) { + print $fh " //\n"; + print $fh " SP_CELL (".lc($block->{name}).", $block->{name});\n"; + print $fh " SP_PIN (".lc($block->{name}).", clk, clk);\n"; + print $fh " SP_PIN (".lc($block->{name}).", check, check);\n"; + foreach my $var (@{$block->{inputs}}, @{$block->{outputs}}, ) { + print $fh " SP_PIN (".lc($block->{name}).", $var, $var);\n"; + } + } + print $fh "}\n\n"; + + # Sub Implementations + print $fh "//".('='x60)."\n"; + foreach my $block (@Blocks) { + print $fh "SP_CTOR_IMP($block->{name}) {\n"; + print $fh " SC_METHOD(clkPosedge);\n"; + print $fh " sensitive_pos(clk);\n"; + print $fh "}\n\n"; + + print $fh "void $block->{name}::clkPosedge() {\n"; + print $fh @{$block->{preass}}; + print $fh @{$block->{body}}; + print $fh "}\n\n"; + } + + $fh->close(); +} + +###################################################################### + +sub callers { + for (my $i=0; ; $i++) { + my @c = caller($i); + last if !$c[0]; + print "Caller $i: ",join(' ',@c[0..3]),"\n"; + } +} + +####################################################################### +####################################################################### +####################################################################### +####################################################################### +# Code generation/emitting Functions + +sub do_a_test { + local $Depth = 0; + @Commit = (); + %VarsBlock = (); + + my $block = { + name=>"Block".($#Blocks+2), + body=>[], + preass=>[], + inputs=>[], + outputs=>[], + }; + + for (my $i=0; $i<$Opt_BlockStmts; $i++) { + my $treeref = gen_leaf(width=>0); + push @{$block->{body}}, + "\tif ($treeref->{text} != ".$treeref->val_to_text().") if (check) ".stop_text().";\n"; + } + + foreach my $var (keys %VarsBlock) { + push @{$block->{inputs}}, $var + if $VarsBlock{$var}{used} && !$VarsBlock{$var}{set}; + } + + foreach my $var (reverse (sort (keys %Vars))) { + my $varref = $Vars{$var}; + next if $varref->{printedit}; + $varref->{printedit} = 1; + push @{$block->{outputs}}, $var; + push @{$block->{preass}}, sprintf ("\t$var = %s;\n" + ,$varref->{text}); + } + + foreach my $com (@Commit) { + &{$com} or die "%Error: Can't eval:\n$com\n $@ "; + } + + push @Blocks, $block; +} + +sub gen_leaf { + my $inforef = {width=>0, # Anything + need_terminal=>0, + #trunc=>undef, # Allow multiply op + @_}; + + $inforef->{width} ||= rnd_width(); + $inforef->{signed} = ($Opt_Signed && $inforef->{width}>1 && (rnd(100)<$Signed_Pct))?1:0 + if !defined $inforef->{signed}; + print +((" "x$Depth)."Leaf of width $inforef->{width}\n") if $Debug; + my $op = rnd_op($inforef); + + my $treeref = new Vg::Base; + while ((my $key,my $val) = each %{$op}) { + $treeref->{$key} = $val; + } + while ((my $key,my $val) = each %{$inforef}) { + $treeref->{$key} = $val; + } + + local $Depth = $Depth+1; + print "RndSub $treeref->{rnd_sub_text}\n" if $Debug; + $treeref->{rnd_sub}($treeref); + $treeref->tree_dump() if $Debug; + + print "RndPl\n" if $Debug; + $treeref->{pl_sub}($treeref); + print "RndV\n" if $Debug; + $treeref->{text} = $treeref->{v_sub}($treeref); + print "Done\n" if $Debug; + print " Value ",$treeref->{val}," = ",$treeref->val_to_text(),"\n" if $Debug; + #$treeref->tree_dump() if $Debug; + + $treeref->{val_size} = $treeref->{val}->Size; #Debugging + $treeref->{val_text} = $treeref->{val}->to_Hex; #Debugging + + ($treeref->{val}->Size == $treeref->{width}) or die "%Error: Size mismatch,"; + + return $treeref; +} + +sub gen_v { + my $opref = shift; + + my $fmt = $opref->{v}; + $fmt =~ s/%1/%s/g; + $fmt =~ s/%2/%s/g; + $fmt =~ s/%3/%s/g; + $fmt =~ s/%v/%s/g; + $fmt =~ s/%i/%s/g; + $fmt =~ s/%xw/%s/g; + + my $argl = $opref->{v}; + my @args; + while ($argl =~ s/(%xw|%.)//) { + my $arg = $1; + push @args, '$treeref->{op1}{text}' if $arg =~ /%1/; + push @args, '$treeref->{op2}{text}' if $arg =~ /%2/; + push @args, '$treeref->{op3}{text}' if $arg =~ /%3/; + push @args, '$treeref->val_to_text' if $arg =~ /%v/; + push @args, '$treeref->{id}' if $arg =~ /%i/; + push @args, '$treeref->{width}-$treeref->{op1}{width}' if $arg =~ /%xw/; + } + + my $func = ("sub { " + ." my \$treeref = shift;" + ." sprintf(\"$fmt\",".join(',',@args).");" + ."}"); + my $set = ("\$opref->{v_sub} = $func; 1;"); + $opref->{v_sub_text} = $func; # For seeing it in debugging dumps + #print "Op V $opref->{name} $set\n"; + eval($set) or die "%Error: Can't eval:\n$set\n $@ "; +} + +sub escapes { + my $str = shift; + my $cmt = shift; + $str =~ s/%tr/\$treeref/g; + $str =~ s/%tg/\$treeref->{signed}/g; + $str =~ s/%tv/\$treeref->{val}/g; + $str =~ s/%tw/\$treeref->{width}/g; + # + $str =~ s/%1r/\$treeref->{op1}/g; + $str =~ s/%1n/(\$treeref->{op1}{val}->Word_Read(0))/g; + $str =~ s/%1v/\$treeref->{op1}{val}/g; + $str =~ s/%1w/\$treeref->{op1}{width}/g; + # + $str =~ s/%2r/\$treeref->{op2}/g; + $str =~ s/%2n/(\$treeref->{op2}{val}->Word_Read(0))/g; + $str =~ s/%2v/\$treeref->{op2}{val}/g; + $str =~ s/%2w/\$treeref->{op2}{width}/g; + # + $str =~ s/%3r/\$treeref->{op3}/g; + $str =~ s/%3n/(\$treeref->{op3}{val}->Word_Read(0))/g; + $str =~ s/%3v/\$treeref->{op3}{val}/g; + $str =~ s/%3w/\$treeref->{op3}{width}/g; + # + $str =~ s/%i/\$treeref->{id}/g; + ($str !~ /%/) or die "%Error: $cmt: Unknown %% escape in $str,"; + return $str; +} + +sub gen_pl { + my $opref = shift; + + my $str = escapes($opref->{pl}, $opref->{name}); + my $func = ("sub { " + ." my \$treeref = shift;" + ." $str;" + ."}"); + my $set = ("\$opref->{pl_sub} = $func; 1;"); + $opref->{pl_sub_text} = $func; # For seeing it in debugging dumps + #print "Op PL $opref->{name} $set\n"; + eval($set) or die "%Error: Can't eval:\n$set\n $@ "; +} + +sub gen_rnd { + my $opref = shift; + + my $str = escapes($opref->{rnd}, $opref->{name}); + + my $func = ("sub { " + ." my \$treeref = shift;" + ." $str;" + ."}"); + my $set = ("\$opref->{rnd_sub} = $func; 1;"); + $opref->{rnd_sub_text} = $func; # For seeing it in debugging dumps + #print "Op RND $opref->{name} $set\n"; + eval($set) or die "%Error: Can't eval:\n$set\n $@ "; +} + +sub stop_text { + return $Opt_Sc?'USTOP()':'$stop'; +} + +sub decl_text { + my $var = shift; + my $decl_with = shift; + + my $varref = $Vars{$var}; + if ($Opt_Sc) { + (!$varref->{signed}) or die "%Error: No signed SystemC yet\n"; + my $type = (( ($varref->{val}->Size == 32) && "uint32_t") + || (($varref->{val}->Size == 64) && "uint64_t")); + $type or die "%Error: Unknown Size ".$varref->{val}->Size,","; + return sprintf " %s<%s> %s; //=%s" + , $decl_with, $type, $var, $varref->{val}->to_Hex; + } else { + return sprintf " reg %s [%3d:0] %s %s; //=%s" + , ($varref->{signed}?"signed":" ") + , ($varref->{val}->Size)-1, + , $var + , (rnd(100)<30 ? "/*verilator public*/":(" "x length("/*verilator public*/"))) + , $varref->{val}->to_Hex; + } +} + +####################################################################### +####################################################################### +####################################################################### +####################################################################### +# Math Functions + +sub val_leaf { return {width=>32, signed=>0, val=>Bit::Vector->new_Dec(32,$_[0]), text=>$_[0],}; } + +sub makebool { return (Bit::Vector->new_Dec(1,$_[0])); } +sub newsized { return (Bit::Vector->new($_[0]->Size)); } +sub max { return $_[0]<$_[1] ? $_[1] : $_[0]; } +sub min { return $_[0]>$_[1] ? $_[1] : $_[0]; } + +sub log2 { + for (my $i=31; $i>=0; $i--) { + return $i+1 if $_[0]>(1<<$i); + } + return 0; +} + +sub countones { + my $out = 0; + for (my $bit=0; $bit < $_[0]->Size; $bit++) { + $out ++ if $_[0]->bit_test($bit); + } + return $out; +} + + +sub VLOGNOT { $_[0]{val} = makebool(($_[1]->is_empty)?1:0); } +sub VUNARYMIN { $_[0]{val} = my $o = newsized($_[1]); $o->Negate($_[1]); } +sub VCOUNTONES { $_[0]{val} = Bit::Vector->new_Dec(32,countones($_[1])); } +sub VONEHOT { $_[0]{val} = makebool((countones($_[1])==1)?1:0); } +sub VONEHOT0 { $_[0]{val} = makebool((countones($_[1])<=1)?1:0); } +sub VLOGAND { $_[0]{val} = makebool((!($_[1]->is_empty) && !($_[2]->is_empty))?1:0); } +sub VLOGOR { $_[0]{val} = makebool((!($_[1]->is_empty) || !($_[2]->is_empty))?1:0); } + +sub VCOND { if (!($_[1]->is_empty)) { $_[0]{val}=$_[2]->Clone; } else { $_[0]{val}=$_[3]->Clone; } } + +sub VREDAND { $_[0]{val} = makebool(($_[1]->is_full)?1:0); } +sub VREDOR { $_[0]{val} = makebool(($_[1]->is_empty)?0:1); } +sub VREDNAND { $_[0]{val} = makebool(($_[1]->is_full)?0:1); } +sub VREDNOR { $_[0]{val} = makebool(($_[1]->is_empty)?1:0); } +sub VREDXOR { + my $out = 0; + for (my $bit=0; $bit < $_[1]->Size; $bit++) { + $out ^= $_[1]->bit_test($bit); + } + $_[0]{val} = makebool($out); +} +sub VREDXNOR { + my $out = 1; + for (my $bit=0; $bit < $_[1]->Size; $bit++) { + $out ^= $_[1]->bit_test($bit); + } + $_[0]{val} = makebool($out); +} +sub eithercompare { ($_[1]->{signed} && $_[2]->{signed}) + ? $_[1]{val}->Compare($_[2]{val}) + : $_[1]{val}->Lexicompare($_[2]{val}); } +sub VEQ { $_[0]{val} = makebool( (eithercompare($_[0],$_[1],$_[2])==0) ?1:0); } +sub VNE { $_[0]{val} = makebool( (eithercompare($_[0],$_[1],$_[2])!=0) ?1:0); } +sub VLT { $_[0]{val} = makebool( (eithercompare($_[0],$_[1],$_[2])< 0) ?1:0); } +sub VLE { $_[0]{val} = makebool( (eithercompare($_[0],$_[1],$_[2])<=0) ?1:0); } +sub VGT { $_[0]{val} = makebool( (eithercompare($_[0],$_[1],$_[2])> 0) ?1:0); } +sub VGE { $_[0]{val} = makebool( (eithercompare($_[0],$_[1],$_[2])>=0) ?1:0); } + +sub VSHIFTLxx { + print "$Vars{vq}->ShiftL($_[0],$_[1]);\n"; + print " ",$_[0]->to_Hex," ",$_[1]->to_Hex,";\n"; + my $out = $_[0]->Clone; + $out->Move_Left($_[1]->Word_Read(0)); + print $out->to_Hex,"\n"; + return $out; } +sub VAND { $_[0]{val}=my $o=newsized($_[1]); $o->Intersection($_[1],$_[2]); } +sub VOR { $_[0]{val}=my $o=newsized($_[1]); $o->Union($_[1],$_[2]); } +sub VNAND { $_[0]{val}=my $o=newsized($_[1]); $o->Intersection($_[1],$_[2]); $o->Complement($o); } +sub VNOR { $_[0]{val}=my $o=newsized($_[1]); $o->Union($_[1],$_[2]); $o->Complement($o); } +sub VXOR { $_[0]{val}=my $o=newsized($_[1]); $o->ExclusiveOr($_[1],$_[2]); } +sub VXNOR{ $_[0]{val}=my $o=newsized($_[1]); $o->ExclusiveOr($_[1],$_[2]); $o->Complement($o); } +sub VNOT { $_[0]{val}=my $o=newsized($_[1]); $o->Complement($_[1]); } +sub VSHIFTL{ $_[0]{val}=my $o=$_[1]->Clone; $o->Move_Left ($_[2]->Word_Read(0)); } +sub VSHIFTR{ $_[0]{val}=my $o=$_[1]->Clone; $o->Move_Right($_[2]->Word_Read(0)); } +sub VSHIFTRS{$_[0]{val}=my $o=$_[1]->Clone; $o->Move_Right($_[2]->Word_Read(0)); + if ($_[1]->msb() && $_[2]->Word_Read(0)>0) {$o->Interval_Fill(max(0,$o->Size-1-$_[2]->Word_Read(0)), $o->Size-1); } + #print (" SHI ",$_[0]{val}->to_Hex,' = ',$_[1]->to_Hex,' >>> ',$_[2]->Word_Read(0),"\n"); +} +sub VCLONE { $_[0]{val}=$_[1]->Clone; } +sub VRESIZE { + $_[0]{val}=$_[1]->Clone; + $_[0]{val}->Resize($_[0]{width}); +} +sub VADD { $_[0]{val}=my $o=newsized($_[1]); $o->add($_[1],$_[2],0); } +sub VSUB { $_[0]{val}=my $o=newsized($_[1]); $o->subtract($_[1],$_[2],0); } +sub VMUL { # Multiply is signed, so need an additional sign bit + my $a=$_[1]->Clone; $a->Resize($_[1]->Size + 1); + my $b=$_[2]->Clone; $b->Resize($_[1]->Size + 1); + my $mo=Bit::Vector->new($_[1]->Size + $_[2]->Size + 1); + $mo->Multiply($a,$b); + my $o=newsized($_[1]); $o->Interval_Copy($mo,0,0,$_[1]->Size); + $_[0]{val}=$o; + } +sub VDIV { my $a=$_[1]->Clone; my $b=$_[2]->Clone; # Else it will take them as signed #s + $a->Resize($a->Size + 1); $b->Resize($b->Size + 1); + print ("//DIVpp ",$_[1]->to_Hex,' ',$_[2]->to_Hex,' ',$_[1]->Size,'.',$_[2]->Size," \n"); + print ("//DIVpp ",$a->to_Hex,' ',$b->to_Hex,' ',$a->Size,'.',$b->Size," \n"); + my $o=newsized($a); my $rem=newsized($a); + if (!$_[2]->is_empty) { $o->Divide($a,$b,$rem); } # No division by zero + #push @Lines, ("//DIV ",$_[1]{val}->to_Hex,' ',$_[2]->to_Hex,' ',$o->to_Hex,'.',$rem->to_Hex," \n"); + $_[0]{val}=$o; } +sub VPOW { # Power is a signed operation + my $a=$_[1]{val}->Clone; if (!$_[1]->{Signed}) { $a->Resize($_[1]{val}->Size + 1); } + my $b=$_[2]{val}->Clone; if (!$_[2]->{Signed}) { $b->Resize($_[2]{val}->Size + 1); } + print "VVpow = ",$_[1]{val}->to_Hex," ** ",$_[2]{val}->to_Hex,"\n"; + my $mo=Bit::Vector->new($_[1]{val}->Size + 1); + $mo->Power($a,$b); + my $o=Bit::Vector->new($_[0]{width}); $o->Interval_Copy($mo,0,0,$_[1]{val}->Size); + $_[0]{val}=$o; + print "VV = $o\n"; +} +sub VRANGE { #print "RANGE ",$_[1]->to_Hex,' ',$_[2]->to_Hex,' ',$_[3]->to_Hex," \n"; + return VRANGE_CONST($_[0],$_[1],$_[2]->Word_Read(0),$_[3]->Word_Read(0)); } +sub VBITSELP { + return VRANGE_CONST($_[0],$_[1],$_[2]->Word_Read(0)+$_[3]->Word_Read(0)-1, $_[2]->Word_Read(0)); } +sub VBITSELM { + return VRANGE_CONST($_[0],$_[1],$_[2]->Word_Read(0), $_[2]->Word_Read(0)-$_[3]->Word_Read(0)+1); } +sub VRANGE_CONST { #print "RANGE ",$_[1]->to_Hex,' ',$_[2],' ',$_[3]," \n"; + my $size = $_[2] - $_[3] + 1; + my $o=Bit::Vector->new($size); + if ($_[3] < $_[1]->Size) { + $o->Interval_Copy($_[1],0,$_[3],$size); + } + $_[0]{val}=$o; } +sub VCONCAT { my $o=Bit::Vector->new($_[1]->Size + $_[2]->Size); + $o->Interval_Copy($_[1],$_[2]->Size,0,$_[1]->Size); + $o->Interval_Copy($_[2],0,0,$_[2]->Size); + $_[0]{val}=$o; } +sub VREPLIC { my $o=Bit::Vector->new($_[1]->Word_Read(0) * $_[2]->Size); + my $pos = 0; + for (my $time=0; $time<($_[1]->Word_Read(0)); $time++) { + $o->Interval_Copy($_[2],$pos,0,$_[2]->Size); + $pos += $_[2]->Size; + } + $_[0]{val}=$o; } + +####################################################################### +####################################################################### +####################################################################### + +package Vg::Base; +use Data::Dumper; +use strict; + +#------------------------------------------------------------ +# CREATORS + +sub new { + my $class = shift; + my $self = { + width=>0, # Width of expression, 0=Pick a width + #signed=>0/1, # Undef = pick a sign + @_}; + bless $self, $class; + return $self; +} + +# ACCESSORS + +#------------------------------------------------------------ +# OUTPUTTING + +sub val_to_text { + my $treeref = shift; + my $val = lc $treeref->{val}->to_Hex(); + $val = "0" if $treeref->{val}->is_empty; + if ($Opt_Sc) { + return ("0x".$val + .(($treeref->{width}>32)?"ULL":"UL") + ); + } else { + return ($treeref->{width} + .($treeref->{signed}?"'sh":"'h") + .$val); + } +} + +sub tree_dump { + my $treeref = shift; + print Dumper($treeref); +} + +####################################################################### +__END__ + +=pod + +=head1 NAME + +vgen.pl - Generate random verilog code + +=head1 SYNOPSIS + + vgen.pl + +=head1 DESCRIPTION + +vgen.pl generates automatic random verilog programs. + +=head1 ARGUMENTS + +=over 4 + +=item --help + +Displays this message and program version and exits. + +=item --blockstmts + +Number of statements per block. Defaults to 2. + +=item --depth + +Maximum depth of generated expressions. + +=item --initial + +Put all statements into an initial block. This will probably be optimized +down to a NOP. + +=item --numops + +Number of operations to create. + +=item --raise + +Pick the specified number of random opcodes, and raise their frequency. + +=item --sc + +Output SystemC code (Experimental). + +=item --seed + +Seed for the random number generator. Defaults to 5, 0=randomize. + +=item --signed + +Include some signed arithmetic in the generated code. Experimental. + +=back + +=head1 SEE ALSO + +=head1 AUTHORS + +Wilson Snyder + +=cut + +###################################################################### +### Local Variables: +### compile-command: "./vgen.pl -sc --depth=10 --blockstmts=10" +### compile-command: "make " +### End: